├── var ├── version ├── created ├── name ├── title ├── organization ├── contact ├── authors ├── summary ├── copyrights ├── requirements ├── resources └── description ├── Gemfile ├── demo ├── applique │ └── no_backsies.rb ├── callbacks │ ├── 10_inherited.rdoc │ ├── 01_method_added.rdoc │ ├── 08_included.rdoc │ ├── 09_extended.rdoc │ ├── 06_singleton_method_undefined.rdoc │ ├── 04_singleton_method_added.rdoc │ ├── 02_method_removed.rdoc │ ├── 03_method_undefined.rdoc │ ├── 07_const_missing.rdoc │ └── 05_singleton_method_removed.rdoc ├── 01_example.rdoc ├── 02_express.rdoc └── 03_options.rdoc ├── .gitignore ├── .qed └── simplecov.rb ├── .yardopts ├── .travis.yml ├── Assembly ├── work └── Profile ├── NOTICE.rdoc ├── MANIFEST ├── HISTORY.rdoc ├── .ruby ├── COPYING.rdoc ├── README.rdoc ├── .gemspec ├── DEMO.rdoc └── lib └── no_backsies.rb /var/version: -------------------------------------------------------------------------------- 1 | 0.3.2 2 | -------------------------------------------------------------------------------- /var/created: -------------------------------------------------------------------------------- 1 | 2011-04-29 2 | -------------------------------------------------------------------------------- /var/name: -------------------------------------------------------------------------------- 1 | no_backsies 2 | -------------------------------------------------------------------------------- /var/title: -------------------------------------------------------------------------------- 1 | NoBacksies 2 | -------------------------------------------------------------------------------- /var/organization: -------------------------------------------------------------------------------- 1 | Rubyworks 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gemspec 3 | -------------------------------------------------------------------------------- /var/contact: -------------------------------------------------------------------------------- 1 | trans 2 | -------------------------------------------------------------------------------- /var/authors: -------------------------------------------------------------------------------- 1 | --- 2 | - 7rans 3 | -------------------------------------------------------------------------------- /var/summary: -------------------------------------------------------------------------------- 1 | Better handling of Ruby callbacks 2 | -------------------------------------------------------------------------------- /demo/applique/no_backsies.rb: -------------------------------------------------------------------------------- 1 | require 'no_backsies' 2 | 3 | -------------------------------------------------------------------------------- /var/copyrights: -------------------------------------------------------------------------------- 1 | --- 2 | - (c) 2009 Rubyworks (BSD-2-Clause) 3 | 4 | -------------------------------------------------------------------------------- /var/requirements: -------------------------------------------------------------------------------- 1 | --- 2 | - qed (test) 3 | - detroit (build) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .reap/digest 2 | .yardoc 3 | doc 4 | log 5 | pkg 6 | tmp 7 | web 8 | DEMO.* 9 | -------------------------------------------------------------------------------- /.qed/simplecov.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start do 3 | coverage_dir 'log/coverage' 4 | end 5 | 6 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --output-dir doc 2 | --title NoBacksies 3 | --protected 4 | --private 5 | lib/ 6 | - 7 | [A-Z]*.* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | rvm: 3 | - 1.8.7 4 | - 1.9.2 5 | - 1.9.3 6 | - jruby 7 | - ree 8 | #- rbx-2.0 9 | script: "bundle exec qed" 10 | 11 | -------------------------------------------------------------------------------- /var/resources: -------------------------------------------------------------------------------- 1 | --- 2 | home: http://rubyworks.github.com/no_backsies 3 | code: http://github.com/rubyworks/no_backsies 4 | mail: http://googlegroups/group/rubyworks-mailinglist 5 | docs: http://rubydoc.info/gems/no_backsies/frames 6 | -------------------------------------------------------------------------------- /var/description: -------------------------------------------------------------------------------- 1 | NoBacksies is a callback layer built on top of Ruby's built-in callback 2 | methods. It makes it possible to add new callbacks very easily, without 3 | having to fuss with more nuanced issues of defining and redefining callback 4 | methods. 5 | 6 | -------------------------------------------------------------------------------- /demo/callbacks/10_inherited.rdoc: -------------------------------------------------------------------------------- 1 | = Inherited 2 | 3 | Given a class that defines a no-backsies +inherited+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :inherited do |base| 13 | list << base 14 | end 15 | end 16 | 17 | z = Class.new(Y) 18 | 19 | Then the results are as follows. 20 | 21 | Y.list #=> [z] 22 | 23 | -------------------------------------------------------------------------------- /demo/callbacks/01_method_added.rdoc: -------------------------------------------------------------------------------- 1 | == Method Added 2 | 3 | Given a class that defines a no-backsies +method_added+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :method_added do |method| 13 | list << method 14 | end 15 | 16 | def foo; end 17 | def bar; end 18 | end 19 | 20 | Then the results are as follows. 21 | 22 | Y.list #=> [:foo, :bar] 23 | 24 | -------------------------------------------------------------------------------- /demo/callbacks/08_included.rdoc: -------------------------------------------------------------------------------- 1 | = Included 2 | 3 | Given a class that defines a no-backsies +included+ callback. 4 | 5 | module U 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :included do |mod| 13 | list << mod 14 | end 15 | end 16 | 17 | class X 18 | include U 19 | end 20 | 21 | class Y 22 | include U 23 | end 24 | 25 | Then the results are as follows. 26 | 27 | U.list #=> [X, Y] 28 | 29 | -------------------------------------------------------------------------------- /demo/callbacks/09_extended.rdoc: -------------------------------------------------------------------------------- 1 | = Extended 2 | 3 | Given a class that defines a no-backsies +extended+ callback. 4 | 5 | module U 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :extended do |mod| 13 | list << mod 14 | end 15 | end 16 | 17 | class X 18 | extend U 19 | end 20 | 21 | class Y 22 | extend U 23 | end 24 | 25 | Then the results are as follows. 26 | 27 | U.list #=> [X, Y] 28 | 29 | -------------------------------------------------------------------------------- /demo/callbacks/06_singleton_method_undefined.rdoc: -------------------------------------------------------------------------------- 1 | == Singleton Method Undefined 2 | 3 | class Y 4 | include NoBacksies::Callbacks 5 | 6 | def self.list 7 | @list ||= [] 8 | end 9 | 10 | callback :singleton_method_undefined do |method| 11 | list << method 12 | end 13 | 14 | def self.foo; end 15 | def self.bar; end 16 | 17 | class << self 18 | undef_method(:foo) 19 | undef_method(:bar) 20 | end 21 | end 22 | 23 | Y.list #=> [:foo, :bar] 24 | 25 | -------------------------------------------------------------------------------- /demo/callbacks/04_singleton_method_added.rdoc: -------------------------------------------------------------------------------- 1 | == Singleton Method Added 2 | 3 | Given a class that defines a no-backsies +singleton_method_added+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :singleton_method_added do |method| 13 | list << method 14 | end 15 | 16 | def self.foo; end 17 | def self.bar; end 18 | end 19 | 20 | Then the results are as follows. 21 | 22 | Y.list #=> [:foo, :bar] 23 | 24 | -------------------------------------------------------------------------------- /demo/callbacks/02_method_removed.rdoc: -------------------------------------------------------------------------------- 1 | == Method Removed 2 | 3 | Given a class that defines a no-backsies +method_removed+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :method_removed do |method| 13 | list << method 14 | end 15 | 16 | def foo; end 17 | def bar; end 18 | 19 | remove_method(:foo) 20 | remove_method(:bar) 21 | end 22 | 23 | Then the results are as follows. 24 | 25 | Y.list #=> [:foo, :bar] 26 | 27 | -------------------------------------------------------------------------------- /demo/callbacks/03_method_undefined.rdoc: -------------------------------------------------------------------------------- 1 | == Method Undefined 2 | 3 | Given a class that defines a no-backsies +method_undefined+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :method_undefined do |method| 13 | list << method 14 | end 15 | 16 | def foo; end 17 | def bar; end 18 | 19 | undef_method(:foo) 20 | undef_method(:bar) 21 | end 22 | 23 | Then the results are as follows. 24 | 25 | Y.list #=> [:foo, :bar] 26 | 27 | -------------------------------------------------------------------------------- /Assembly: -------------------------------------------------------------------------------- 1 | --- 2 | github: 3 | gh_pages: web 4 | 5 | gem: {} 6 | 7 | dnote: 8 | title: Developer's Notes 9 | output: log/NOTES.rdoc 10 | 11 | yard: 12 | yardopts: true 13 | priority: -1 14 | 15 | qed: 16 | files: demo/ 17 | 18 | qedoc: 19 | files : demo/ 20 | title : No Backsies 21 | output: 22 | - DEMO.rdoc 23 | - web/demo.html 24 | 25 | email: 26 | mailto : 27 | - ruby-talk@ruby-lang.org 28 | - rubyworks-mailinglist@googlegroups.com 29 | 30 | vclog: 31 | output: 32 | - log/History.rdoc 33 | - log/Changes.rdoc 34 | 35 | -------------------------------------------------------------------------------- /demo/01_example.rdoc: -------------------------------------------------------------------------------- 1 | = Basic Example 2 | 3 | First require the NoBacksies library. 4 | 5 | require 'no_backsies' 6 | 7 | Include the Callbacks module in a class and define 8 | a callback procedure. 9 | 10 | class Y 11 | include NoBacksies::Callbacks 12 | 13 | def self.list 14 | @list ||= [] 15 | end 16 | 17 | callback :method_added do |method| 18 | list << method 19 | end 20 | 21 | def foo; end 22 | def bar; end 23 | end 24 | 25 | We can see that +list+ holds the methods added. 26 | 27 | Y.list.assert == [:foo, :bar] 28 | 29 | -------------------------------------------------------------------------------- /demo/callbacks/07_const_missing.rdoc: -------------------------------------------------------------------------------- 1 | = Constant Missing 2 | 3 | Given a class that defines a no-backsies +const_missing+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :const_missing, :superless=>true do |const| 13 | list << const 14 | end 15 | 16 | FOO 17 | BAR 18 | 19 | end 20 | 21 | Then the results are as follows. 22 | 23 | Y.list #=> [:FOO, :BAR] 24 | 25 | Notice we used `:superless` to prevent super from being called, which would 26 | have raised an NameError. 27 | -------------------------------------------------------------------------------- /demo/callbacks/05_singleton_method_removed.rdoc: -------------------------------------------------------------------------------- 1 | == Singleton Method Removed 2 | 3 | Given a class that defines a no-backsies +singleton_method_removed+ callback. 4 | 5 | class Y 6 | include NoBacksies::Callbacks 7 | 8 | def self.list 9 | @list ||= [] 10 | end 11 | 12 | callback :singleton_method_removed do |method| 13 | list << method 14 | end 15 | 16 | def self.foo; end 17 | def self.bar; end 18 | 19 | class << self 20 | remove_method(:foo) 21 | remove_method(:bar) 22 | end 23 | end 24 | 25 | Then the results are as follows. 26 | 27 | Y.list #=> [:foo, :bar] 28 | 29 | -------------------------------------------------------------------------------- /work/Profile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | name 'nobacksies' 4 | title 'NoBacksies' 5 | summary 'Improve Control Over Ruby Callbacks' 6 | license 'Apache 2.0' 7 | contact 'trans ' 8 | created '2011-04-29' 9 | 10 | authors ['Thomas Sawyer'] 11 | 12 | description %{ 13 | NoBacksies is a small library that makes it much easier 14 | to work with Ruby's built-int callbacks. 15 | }.strip 16 | 17 | company 'Rubyworks' 18 | copyright 'Copyright (c) 2008 Thomas Sawyer' 19 | 20 | version File.read('VERSION').strip 21 | date Time.now 22 | 23 | manifest 'MANIFEST' 24 | 25 | gem 'syckle (build)' 26 | gem 'qed (test)' 27 | 28 | -------------------------------------------------------------------------------- /NOTICE.rdoc: -------------------------------------------------------------------------------- 1 | = COPYRIGHT NOTICES 2 | 3 | == No Backsies 4 | 5 | Copyright (c) 2011 Thomas Sawyer 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | 19 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | #!mast .ruby .yardopts bin lib man qed spec test [A-Z]*.* 2 | .ruby 3 | .yardopts 4 | lib/no_backsies.rb 5 | qed/01_example.rdoc 6 | qed/02_express.rdoc 7 | qed/03_options.rdoc 8 | qed/applique/no_backsies.rb 9 | qed/callbacks/01_method_added.rdoc 10 | qed/callbacks/02_method_removed.rdoc 11 | qed/callbacks/03_method_undefined.rdoc 12 | qed/callbacks/04_singleton_method_added.rdoc 13 | qed/callbacks/05_singleton_method_removed.rdoc 14 | qed/callbacks/06_singleton_method_undefined.rdoc 15 | qed/callbacks/07_const_missing.rdoc 16 | qed/callbacks/08_included.rdoc 17 | qed/callbacks/09_extended.rdoc 18 | qed/callbacks/10_inherited.rdoc 19 | HISTORY.rdoc 20 | README.rdoc 21 | QED.rdoc 22 | COPYING.rdoc 23 | NOTICE.rdoc 24 | -------------------------------------------------------------------------------- /demo/02_express.rdoc: -------------------------------------------------------------------------------- 1 | = Callback Expression 2 | 3 | NoBacksies makes it easier to control callback expression. This 4 | is useful in the prevention of infinite recursion. For instance, 5 | infinite recursion is a common problem when a +method_added+ callback 6 | defines a new method. 7 | 8 | Here is an example that demonstrates how to work around this problem 9 | using the +callback_express+ method. 10 | 11 | class Z 12 | include NoBacksies::Callbacks 13 | 14 | callback :method_added do |method| 15 | callback_express :method_added=>false do 16 | define_method("#{method}!") do 17 | send(method) + "!" 18 | end 19 | end 20 | end 21 | 22 | def foo; "foo"; end 23 | def bar; "bar"; end 24 | end 25 | 26 | In this example, a new `Z` object will get an automatically defined bang method 27 | for every explicitly defined method. 28 | 29 | z = Z.new 30 | z.foo #=> "foo" 31 | z.foo! #=> "foo!" 32 | z.bar #=> "bar" 33 | z.bar! #=> "bar!" 34 | 35 | -------------------------------------------------------------------------------- /HISTORY.rdoc: -------------------------------------------------------------------------------- 1 | = RELEASE HISTORY 2 | 3 | == 0.3.2 | 2011-10-27 4 | 5 | This release adds support for calling super from within callback procedures. 6 | 7 | Changes: 8 | 9 | * Add support for calling super. 10 | 11 | 12 | == 0.3.1 | 2011-07-06 13 | 14 | Fix method argumements on const_missing, included, extended 15 | and inherited callbacks. 16 | 17 | Changes: 18 | 19 | * Fixed callback arguments that have been copy/pasted. (blambeau) 20 | * Add individual tests for each callback. 21 | 22 | 23 | == 0.3.0 | 2011-07-06 24 | 25 | This release is made primarily because the program is now 26 | distributed under the BSD 2-Clause license. 27 | 28 | Changes: 29 | 30 | * Changed licenses to BSD 2-Clause. 31 | * Cleaned up project files. 32 | 33 | 34 | == 0.2.0 | 2011-04-29 (Later on Wedding Day) 35 | 36 | You know, one of thsoe "oops", kind of things. 37 | 38 | 39 | == 0.1.0 | 2011-04-29 (Wedding Day) 40 | 41 | This is the initial release of NoBacksies. 42 | 43 | Changes: 44 | 45 | * Happy Wedding Day! 46 | 47 | -------------------------------------------------------------------------------- /.ruby: -------------------------------------------------------------------------------- 1 | --- 2 | source: 3 | - var 4 | authors: 5 | - name: 7rans 6 | email: transfire@gmail.com 7 | copyrights: 8 | - holder: Rubyworks 9 | year: '2009' 10 | license: BSD-2-Clause 11 | replacements: [] 12 | alternatives: [] 13 | requirements: 14 | - name: qed 15 | groups: 16 | - test 17 | development: true 18 | - name: detroit 19 | groups: 20 | - build 21 | development: true 22 | dependencies: [] 23 | conflicts: [] 24 | repositories: [] 25 | resources: 26 | home: http://rubyworks.github.com/no_backsies 27 | code: http://github.com/rubyworks/no_backsies 28 | mail: http://googlegroups/group/rubyworks-mailinglist 29 | docs: http://rubydoc.info/gems/no_backsies/frames 30 | extra: {} 31 | load_path: 32 | - lib 33 | revision: 0 34 | created: '2011-04-29' 35 | summary: Better handling of Ruby callbacks 36 | title: NoBacksies 37 | version: 0.3.2 38 | name: no_backsies 39 | description: ! 'NoBacksies is a callback layer built on top of Ruby''s built-in callback 40 | 41 | methods. It makes it possible to add new callbacks very easily, without 42 | 43 | having to fuss with more nuanced issues of defining and redefining callback 44 | 45 | methods.' 46 | organization: Rubyworks 47 | date: '2011-11-11' 48 | -------------------------------------------------------------------------------- /demo/03_options.rdoc: -------------------------------------------------------------------------------- 1 | = Callback Options 2 | 3 | == Once Callback Option 4 | 5 | NoBacksies supports special callback options. The first is +:once+. 6 | By setting +:once+ to +true+, the callback will be used one time and 7 | then removed. 8 | 9 | class Q 10 | include NoBacksies::Callbacks 11 | 12 | def self.list 13 | @list ||= [] 14 | end 15 | 16 | callback :method_added, :once=>true do |method| 17 | list << method 18 | end 19 | 20 | def foo; "foo"; end 21 | def bar; "bar"; end 22 | end 23 | 24 | Here we see that only :foo has been added to the list. 25 | 26 | Q.list #=> [:foo] 27 | 28 | == Safe Callback Option 29 | 30 | The other option supported is +:safe+. When +:safe+ is set to true 31 | the callback is automatically wrapped in an #callback_express block 32 | that sets the expression of the callback to false while it is being 33 | processed. This means we can rewrite our first example more succinctly. 34 | 35 | class Z2 36 | include NoBacksies::Callbacks 37 | 38 | callback :method_added, :safe=>true do |method| 39 | define_method("#{method}!") do 40 | send(method) + "!" 41 | end 42 | end 43 | 44 | def foo; "foo"; end 45 | def bar; "bar"; end 46 | end 47 | 48 | In this example, a new `Z` object will get an automatically defined bang method 49 | for every explicitly defined method. 50 | 51 | z = Z2.new 52 | z.foo #=> "foo" 53 | z.foo! #=> "foo!" 54 | z.bar #=> "bar" 55 | z.bar! #=> "bar!" 56 | 57 | -------------------------------------------------------------------------------- /COPYING.rdoc: -------------------------------------------------------------------------------- 1 | = COPYRIGHT NOTICES 2 | 3 | == No Backsies 4 | 5 | Copyright:: (c) 2011 Thomas Sawyer, Rubyworks 6 | License:: BSD-2-Clause 7 | 8 | Copyright (c) 2011 Thomas Sawyer. All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, are 11 | permitted provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright notice, this list of 14 | conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 17 | of conditions and the following disclaimer in the documentation and/or other materials 18 | provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY Thomas Sawyer ``AS IS'' AND ANY EXPRESS OR IMPLIED 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Thomas Sawyer OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | The views and conclusions contained in the software and documentation are those of the 31 | authors and should not be interpreted as representing official policies, either expressed 32 | or implied, of Thomas Sawyer. 33 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = No Backsies 2 | 3 | {Homepage}[http://rubyworks.github.com/no_backsies] | 4 | {Development}[http://github.com/rubyworks/no_backsies] | 5 | {Mailing List}[http://groups.google.com/group/rubyworks-mailinglist] | 6 | {API Reference}[http://rubydoc.info/gems/no_backsies/frames] | 7 | {Demonstrandum}[http://rubyworks.github.com/no_backsies/qed.html] 8 | 9 | 10 | == DESCRIPTION 11 | 12 | NoBacksies is a callback layer built on top of Ruby's built-in callback 13 | methods. It makes it possible to add new callbacks very easily, without 14 | having to fuss with more nuanced issues of defining and redefining callback 15 | methods. 16 | 17 | 18 | == Compatability 19 | 20 | NoBacksies does not currently work with *Rubinius*. Anyone know why? 21 | 22 | 23 | == EXAMPLES 24 | 25 | Here is a very simple example of how NoBacksies works. 26 | 27 | class Y 28 | include NoBacksies::Callbacks 29 | 30 | def self.list 31 | @list ||= [] 32 | end 33 | 34 | callback :method_added do |method| 35 | list << method 36 | end 37 | 38 | def foo; end 39 | def bar; end 40 | end 41 | 42 | Y.list #=> [:foo, :bar] 43 | 44 | Here is another example taken from the Anise[http://rubyworks.github.com/anise] 45 | project. 46 | 47 | class Y 48 | include Anise 49 | include NoBacksies 50 | 51 | def self.doc(string) 52 | callback :method_added, :once=>true do |method| 53 | self.ann(method, :doc=>string) 54 | end 55 | end 56 | 57 | doc "foo is cool" 58 | def foo 59 | # ... 60 | end 61 | end 62 | 63 | Y.ann(:foo, :doc) #=> "foo is cool" 64 | 65 | See the QED documentation for more examples. 66 | 67 | 68 | == INSTALLATION 69 | 70 | Install the RubyGems package in the usual fashion. 71 | 72 | $ gem install no_backsies 73 | 74 | 75 | == CONTRIBUTE 76 | 77 | === Development 78 | 79 | Source code is hosted on GitHub[http://github.com/rubyworks/qed]. 80 | If you'd like to submit a patch please fork the repository and 81 | submit a pull request (ideally in a topic branch). 82 | 83 | === Testing 84 | 85 | QED[http://rubyworks.github.com/qed] is used to testing. 86 | 87 | $ gem install qed 88 | 89 | To run the tests: 90 | 91 | $ qed [path/to/test] 92 | 93 | === Donations 94 | 95 | Software development is very time consuming. Rubyworks does a lot of FOSS 96 | development for el $0. We do it for the love of programming and 97 | for Ruby. Any dollars that do come our way help us continue 98 | the effort. So anything you may offer will help these projects such as this 99 | continue to flourish. 100 | 101 | See the {Rubyworks Homepage}[http://rubyworks.github.com]. 102 | 103 | Thanks. 104 | 105 | 106 | == LEGAL 107 | 108 | (BSD 2 License) 109 | 110 | Copyright (c) 2011 Thomas Sawyer 111 | 112 | Unless otherwise negotiated with the original author, NoBacksies is 113 | distributable under the terms of the BSD 2-clause license. 114 | 115 | See the LICENSE.rdoc file for details. 116 | -------------------------------------------------------------------------------- /.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'yaml' 4 | 5 | module DotRuby 6 | 7 | # 8 | class GemSpec 9 | 10 | # For which revision of .ruby is this gemspec intended? 11 | REVISION = 0 12 | 13 | # 14 | PATTERNS = { 15 | :bin_files => 'bin/*', 16 | :lib_files => 'lib/{**/}*.rb', 17 | :ext_files => 'ext/{**/}extconf.rb', 18 | :doc_files => '*.{txt,rdoc,md,markdown,tt,textile}', 19 | :test_files => '{test/{**/}*_test.rb,spec/{**/}*_spec.rb}' 20 | } 21 | 22 | # 23 | def self.instance 24 | new.to_gemspec 25 | end 26 | 27 | attr :metadata 28 | 29 | attr :manifest 30 | 31 | # 32 | def initialize 33 | @metadata = YAML.load_file('.ruby') 34 | @manifest = Dir.glob('manifest{,.txt}', File::FNM_CASEFOLD).first 35 | 36 | if @metadata['revision'].to_i != REVISION 37 | warn "You have the wrong revision. Trying anyway..." 38 | end 39 | end 40 | 41 | # 42 | def scm 43 | @scm ||= \ 44 | case 45 | when File.directory?('.git') 46 | :git 47 | end 48 | end 49 | 50 | # 51 | def files 52 | @files ||= \ 53 | #glob_files[patterns[:files]] 54 | case 55 | when manifest 56 | File.readlines(manifest). 57 | map{ |line| line.strip }. 58 | reject{ |line| line.empty? || line[0,1] == '#' } 59 | when scm == :git 60 | `git ls-files -z`.split("\0") 61 | else 62 | Dir.glob('{**/}{.*,*}') # TODO: be more specific using standard locations ? 63 | end.select{ |path| File.file?(path) } 64 | end 65 | 66 | # 67 | def glob_files(pattern) 68 | Dir.glob(pattern).select { |path| 69 | File.file?(path) && files.include?(path) 70 | } 71 | end 72 | 73 | # 74 | def patterns 75 | PATTERNS 76 | end 77 | 78 | # 79 | def executables 80 | @executables ||= \ 81 | glob_files(patterns[:bin_files]).map do |path| 82 | File.basename(path) 83 | end 84 | end 85 | 86 | def extensions 87 | @extensions ||= \ 88 | glob_files(patterns[:ext_files]).map do |path| 89 | File.basename(path) 90 | end 91 | end 92 | 93 | # 94 | def name 95 | metadata['name'] || metadata['title'].downcase.gsub(/\W+/,'_') 96 | end 97 | 98 | # 99 | def to_gemspec 100 | Gem::Specification.new do |gemspec| 101 | gemspec.name = name 102 | gemspec.version = metadata['version'] 103 | gemspec.summary = metadata['summary'] 104 | gemspec.description = metadata['description'] 105 | 106 | metadata['authors'].each do |author| 107 | gemspec.authors << author['name'] 108 | 109 | if author.has_key?('email') 110 | if gemspec.email 111 | gemspec.email << author['email'] 112 | else 113 | gemspec.email = [author['email']] 114 | end 115 | end 116 | end 117 | 118 | gemspec.licenses = metadata['copyrights'].map{ |c| c['license'] }.compact 119 | 120 | metadata['requirements'].each do |req| 121 | name = req['name'] 122 | version = req['version'] 123 | groups = req['groups'] || [] 124 | 125 | case version 126 | when /^(.*?)\+$/ 127 | version = ">= #{$1}" 128 | when /^(.*?)\-$/ 129 | version = "< #{$1}" 130 | when /^(.*?)\~$/ 131 | version = "~> #{$1}" 132 | end 133 | 134 | if groups.empty? or groups.include?('runtime') 135 | # populate runtime dependencies 136 | if gemspec.respond_to?(:add_runtime_dependency) 137 | gemspec.add_runtime_dependency(name,*version) 138 | else 139 | gemspec.add_dependency(name,*version) 140 | end 141 | else 142 | # populate development dependencies 143 | if gemspec.respond_to?(:add_development_dependency) 144 | gemspec.add_development_dependency(name,*version) 145 | else 146 | gemspec.add_dependency(name,*version) 147 | end 148 | end 149 | end 150 | 151 | # convert external dependencies into a requirements 152 | if metadata['external_dependencies'] 153 | ##gemspec.requirements = [] unless metadata['external_dependencies'].empty? 154 | metadata['external_dependencies'].each do |req| 155 | gemspec.requirements << req.to_s 156 | end 157 | end 158 | 159 | # determine homepage from resources 160 | homepage = metadata['resources'].find{ |key, url| key =~ /^home/ } 161 | gemspec.homepage = homepage.last if homepage 162 | 163 | gemspec.require_paths = metadata['load_path'] || ['lib'] 164 | gemspec.post_install_message = metadata['install_message'] 165 | 166 | # RubyGems specific metadata 167 | gemspec.files = files 168 | gemspec.extensions = extensions 169 | gemspec.executables = executables 170 | 171 | if Gem::VERSION < '1.7.' 172 | gemspec.default_executable = gemspec.executables.first 173 | end 174 | 175 | gemspec.test_files = glob_files(patterns[:test_files]) 176 | 177 | unless gemspec.files.include?('.document') 178 | gemspec.extra_rdoc_files = glob_files(patterns[:doc_files]) 179 | end 180 | end 181 | end 182 | 183 | end #class GemSpec 184 | 185 | end 186 | 187 | DotRuby::GemSpec.instance 188 | -------------------------------------------------------------------------------- /DEMO.rdoc: -------------------------------------------------------------------------------- 1 | = Basic Example 2 | 3 | First require the NoBacksies library. 4 | 5 | require 'no_backsies' 6 | 7 | Include the Callbacks module in a class and define 8 | a callback procedure. 9 | 10 | class Y 11 | include NoBacksies::Callbacks 12 | 13 | def self.list 14 | @list ||= [] 15 | end 16 | 17 | callback :method_added do |method| 18 | list << method 19 | end 20 | 21 | def foo; end 22 | def bar; end 23 | end 24 | 25 | We can see that +list+ holds the methods added. 26 | 27 | Y.list.assert == [:foo, :bar] 28 | 29 | 30 | = Callback Expression 31 | 32 | NoBacksies makes it easier to control callback expression. This 33 | is useful in the prevention of infinite recursion. For instance, 34 | infinite recursion is a common problem when a +method_added+ callback 35 | defines a new method. 36 | 37 | Here is an example that demonstrates how to work around this problem 38 | using the +callback_express+ method. 39 | 40 | class Z 41 | include NoBacksies::Callbacks 42 | 43 | callback :method_added do |method| 44 | callback_express :method_added=>false do 45 | define_method("#{method}!") do 46 | send(method) + "!" 47 | end 48 | end 49 | end 50 | 51 | def foo; "foo"; end 52 | def bar; "bar"; end 53 | end 54 | 55 | In this example, a new `Z` object will get an automatically defined bang method 56 | for every explicitly defined method. 57 | 58 | z = Z.new 59 | z.foo #=> "foo" 60 | z.foo! #=> "foo!" 61 | z.bar #=> "bar" 62 | z.bar! #=> "bar!" 63 | 64 | 65 | = Callback Options 66 | 67 | == Once Callback Option 68 | 69 | NoBacksies supports special callback options. The first is +:once+. 70 | By setting +:once+ to +true+, the callback will be used one time and 71 | then removed. 72 | 73 | class Q 74 | include NoBacksies::Callbacks 75 | 76 | def self.list 77 | @list ||= [] 78 | end 79 | 80 | callback :method_added, :once=>true do |method| 81 | list << method 82 | end 83 | 84 | def foo; "foo"; end 85 | def bar; "bar"; end 86 | end 87 | 88 | Here we see that only :foo has been added to the list. 89 | 90 | Q.list #=> [:foo] 91 | 92 | == Safe Callback Option 93 | 94 | The other option supported is +:safe+. When +:safe+ is set to true 95 | the callback is automatically wrapped in an #callback_express block 96 | that sets the expression of the callback to false while it is being 97 | processed. This means we can rewrite our first example more succinctly. 98 | 99 | class Z2 100 | include NoBacksies::Callbacks 101 | 102 | callback :method_added, :safe=>true do |method| 103 | define_method("#{method}!") do 104 | send(method) + "!" 105 | end 106 | end 107 | 108 | def foo; "foo"; end 109 | def bar; "bar"; end 110 | end 111 | 112 | In this example, a new `Z` object will get an automatically defined bang method 113 | for every explicitly defined method. 114 | 115 | z = Z2.new 116 | z.foo #=> "foo" 117 | z.foo! #=> "foo!" 118 | z.bar #=> "bar" 119 | z.bar! #=> "bar!" 120 | 121 | 122 | == Method Added 123 | 124 | Given a class that defines a no-backsies +method_added+ callback. 125 | 126 | class Y 127 | include NoBacksies::Callbacks 128 | 129 | def self.list 130 | @list ||= [] 131 | end 132 | 133 | callback :method_added do |method| 134 | list << method 135 | end 136 | 137 | def foo; end 138 | def bar; end 139 | end 140 | 141 | Then the results are as follows. 142 | 143 | Y.list #=> [:foo, :bar] 144 | 145 | 146 | == Method Removed 147 | 148 | Given a class that defines a no-backsies +method_removed+ callback. 149 | 150 | class Y 151 | include NoBacksies::Callbacks 152 | 153 | def self.list 154 | @list ||= [] 155 | end 156 | 157 | callback :method_removed do |method| 158 | list << method 159 | end 160 | 161 | def foo; end 162 | def bar; end 163 | 164 | remove_method(:foo) 165 | remove_method(:bar) 166 | end 167 | 168 | Then the results are as follows. 169 | 170 | Y.list #=> [:foo, :bar] 171 | 172 | 173 | == Method Undefined 174 | 175 | Given a class that defines a no-backsies +method_undefined+ callback. 176 | 177 | class Y 178 | include NoBacksies::Callbacks 179 | 180 | def self.list 181 | @list ||= [] 182 | end 183 | 184 | callback :method_undefined do |method| 185 | list << method 186 | end 187 | 188 | def foo; end 189 | def bar; end 190 | 191 | undef_method(:foo) 192 | undef_method(:bar) 193 | end 194 | 195 | Then the results are as follows. 196 | 197 | Y.list #=> [:foo, :bar] 198 | 199 | 200 | == Singleton Method Added 201 | 202 | Given a class that defines a no-backsies +singleton_method_added+ callback. 203 | 204 | class Y 205 | include NoBacksies::Callbacks 206 | 207 | def self.list 208 | @list ||= [] 209 | end 210 | 211 | callback :singleton_method_added do |method| 212 | list << method 213 | end 214 | 215 | def self.foo; end 216 | def self.bar; end 217 | end 218 | 219 | Then the results are as follows. 220 | 221 | Y.list #=> [:foo, :bar] 222 | 223 | 224 | == Singleton Method Removed 225 | 226 | Given a class that defines a no-backsies +singleton_method_removed+ callback. 227 | 228 | class Y 229 | include NoBacksies::Callbacks 230 | 231 | def self.list 232 | @list ||= [] 233 | end 234 | 235 | callback :singleton_method_removed do |method| 236 | list << method 237 | end 238 | 239 | def self.foo; end 240 | def self.bar; end 241 | 242 | class << self 243 | remove_method(:foo) 244 | remove_method(:bar) 245 | end 246 | end 247 | 248 | Then the results are as follows. 249 | 250 | Y.list #=> [:foo, :bar] 251 | 252 | 253 | == Singleton Method Undefined 254 | 255 | class Y 256 | include NoBacksies::Callbacks 257 | 258 | def self.list 259 | @list ||= [] 260 | end 261 | 262 | callback :singleton_method_undefined do |method| 263 | list << method 264 | end 265 | 266 | def self.foo; end 267 | def self.bar; end 268 | 269 | class << self 270 | undef_method(:foo) 271 | undef_method(:bar) 272 | end 273 | end 274 | 275 | Y.list #=> [:foo, :bar] 276 | 277 | 278 | = Constant Missing 279 | 280 | Given a class that defines a no-backsies +const_missing+ callback. 281 | 282 | class Y 283 | include NoBacksies::Callbacks 284 | 285 | def self.list 286 | @list ||= [] 287 | end 288 | 289 | callback :const_missing, :superless=>true do |const| 290 | list << const 291 | end 292 | 293 | FOO 294 | BAR 295 | 296 | end 297 | 298 | Then the results are as follows. 299 | 300 | Y.list #=> [:FOO, :BAR] 301 | 302 | Notice we used `:superless` to prevent super from being called, which would 303 | have raised an NameError. 304 | 305 | = Included 306 | 307 | Given a class that defines a no-backsies +included+ callback. 308 | 309 | module U 310 | include NoBacksies::Callbacks 311 | 312 | def self.list 313 | @list ||= [] 314 | end 315 | 316 | callback :included do |mod| 317 | list << mod 318 | end 319 | end 320 | 321 | class X 322 | include U 323 | end 324 | 325 | class Y 326 | include U 327 | end 328 | 329 | Then the results are as follows. 330 | 331 | U.list #=> [X, Y] 332 | 333 | 334 | = Extended 335 | 336 | Given a class that defines a no-backsies +extended+ callback. 337 | 338 | module U 339 | include NoBacksies::Callbacks 340 | 341 | def self.list 342 | @list ||= [] 343 | end 344 | 345 | callback :extended do |mod| 346 | list << mod 347 | end 348 | end 349 | 350 | class X 351 | extend U 352 | end 353 | 354 | class Y 355 | extend U 356 | end 357 | 358 | Then the results are as follows. 359 | 360 | U.list #=> [X, Y] 361 | 362 | 363 | = Inherited 364 | 365 | Given a class that defines a no-backsies +inherited+ callback. 366 | 367 | class Y 368 | include NoBacksies::Callbacks 369 | 370 | def self.list 371 | @list ||= [] 372 | end 373 | 374 | callback :inherited do |base| 375 | list << base 376 | end 377 | end 378 | 379 | z = Class.new(Y) 380 | 381 | Then the results are as follows. 382 | 383 | Y.list #=> [z] 384 | 385 | 386 | -------------------------------------------------------------------------------- /lib/no_backsies.rb: -------------------------------------------------------------------------------- 1 | # NoBacksies module ecapsulates all supported callback mixins. 2 | # 3 | # NoBacksies::Callbacks can be mixed-in and all supported callbacks will 4 | # be applied to the class or module. 5 | # 6 | # class Y 7 | # include NoBacksies::Callbacks 8 | # 9 | # def self.list 10 | # @list ||= [] 11 | # end 12 | # 13 | # callback :method_added do |method| 14 | # list << method 15 | # end 16 | # 17 | # def foo; end 18 | # def bar; end 19 | # end 20 | # 21 | # Y.list.assert #=> [:foo, :bar] 22 | # 23 | # Using callbacks can easily lead to infinite loops. NoBacksies makes it 24 | # easier to control callback expression via the #callback_express 25 | # method. 26 | # 27 | # class Z 28 | # include NoBacksies::Callbacks 29 | # 30 | # def self.list 31 | # @list ||= [] 32 | # end 33 | # 34 | # callback :method_added do |method| 35 | # callback_express :method_added=>false do 36 | # define_method("#{method}!") do 37 | # send(method) + "!" 38 | # end 39 | # end 40 | # end 41 | # 42 | # def foo; "foo"; end 43 | # def bar; "bar"; end 44 | # end 45 | # 46 | # y = Y.new 47 | # y.foo! #=> "foo!" 48 | # 49 | # == Calling Super 50 | # 51 | # Each callback invocation passes along a superblock procedure, which can be 52 | # used to invoke `super` for the underlying callback method. For example, 53 | # it is common to call `super` in a `const_missing` callback if the dynamic 54 | # constant lookup fails. 55 | # 56 | # callback :const_missing do |const, &superblock| 57 | # psuedo_constants[const] || superblock.call 58 | # end 59 | # 60 | # By default, super is called after call callback procedures are called becuase 61 | # that is by far the most commonly desired behavior. To suppress this behavior 62 | # pass the `:superless=>true` flag to the `callback` method. 63 | # 64 | # callback :included, :superless=>true do |mod| 65 | # ... 66 | # end 67 | # 68 | # == Customizing the Underlying Callback Procedure 69 | # 70 | # Every callback follows the same simply pattern, e.g. for `method_added`: 71 | # 72 | # def self.method_added(method) 73 | # if defined?(super) 74 | # callback_invoke(:method_added, method){ super(method) } 75 | # else 76 | # callback_invoke(:method_added, method) 77 | # end 78 | # end 79 | # 80 | # So it is easy enough to customize if you have some special requirements. 81 | # Say you want to call super before all callback procedures, and never allow 82 | # any callback procedure to do so themselves, then you could simply redefine 83 | # the underlying callback method as: 84 | # 85 | # def self.method_added(method) 86 | # super(method) if defined?(super) 87 | # callback_invoke(:method_added, method) 88 | # end 89 | # 90 | # NOTE: Currently the NoBacksies module only supports class level callbacks. 91 | # We will look into adding instance level callbacks, such as `method_missing` 92 | # in a future version. 93 | 94 | module NoBacksies 95 | 96 | # 97 | module Callbacks 98 | # Apply all supported callback modules. 99 | def self.append_features(base) 100 | base.extend CallbackMethods 101 | base.extend MethodAdded 102 | base.extend MethodRemoved 103 | base.extend MethodUndefined 104 | base.extend SingletonMethodAdded 105 | base.extend SingletonMethodRemoved 106 | base.extend SingletonMethodUndefined 107 | base.extend ConstMissing 108 | base.extend Included 109 | base.extend Extended 110 | base.extend Inherited 111 | end 112 | end 113 | 114 | # The CallbackMethods module adds the callback methods which are used 115 | # define and access callback definitions. Mixing-in this module is 116 | # handled automatically, so you do not need to worry with it. In other 117 | # words, consider the module *private*. 118 | 119 | module CallbackMethods 120 | # Define a callback. 121 | def callback(name, options={}, &block) 122 | callbacks[name.to_sym] << [block, options] 123 | end 124 | 125 | # 126 | def callbacks 127 | @_callbacks ||= ( 128 | anc = ancestors[1..-1].find do |anc| 129 | anc.callbacks rescue nil # TODO: Need faster way! 130 | end 131 | anc ? anc.callbacks.dup : Hash.new{|h,k| h[k]=[]} 132 | ) 133 | end 134 | 135 | # Returns Hash of true/false activity state of callbacks. 136 | # 137 | # TODO: Should expression be inherited? 138 | def callback_express(express={}, &block) 139 | @_callback_express ||= Hash.new{|h,k| h[k]=true} 140 | 141 | if block 142 | tmp = @_callback_express.dup 143 | express.each{ |k,v| @_callback_express[k.to_sym] = !!v } 144 | block.call 145 | @_callback_express = tmp 146 | else 147 | express.each{ |k,v| @_callback_express[k.to_sym] = !!v } 148 | end 149 | 150 | @_callback_express 151 | end 152 | 153 | # Invoke a callback. 154 | # 155 | def callback_invoke(name, *args, &superblock) 156 | name = name.to_sym 157 | return unless callback_express[name] 158 | callbacks[name].each do |block, options| 159 | if options[:safe] 160 | callback_express(name=>false) do 161 | block.call(*args, &superblock) 162 | end 163 | else 164 | block.call(*args, &superblock) 165 | end 166 | if options[:once] 167 | callbacks[name].delete([block, options]) 168 | end 169 | if !options[:superless] 170 | superblock.call if superblock 171 | end 172 | end 173 | end 174 | end #module 175 | 176 | # Callback system for #method_added. 177 | module MethodAdded 178 | # 179 | def self.append_features(base) 180 | base.extend CallbackMethods 181 | base.extend self 182 | end 183 | 184 | # 185 | def method_added(method) 186 | if defined?(super) 187 | callback_invoke(:method_added, method){ super(method) } 188 | else 189 | callback_invoke(:method_added, method) 190 | end 191 | end 192 | end 193 | 194 | # Callback system for #method_removed. 195 | module MethodRemoved 196 | # 197 | def self.append_features(base) 198 | base.extend CallbackMethods 199 | base.extend self 200 | end 201 | 202 | # 203 | def method_removed(method) 204 | if defined?(super) 205 | callback_invoke(:method_removed, method){ super(method) } 206 | else 207 | callback_invoke(:method_removed, method) 208 | end 209 | end 210 | end 211 | 212 | # Callback system for #method_removed. 213 | module MethodUndefined 214 | # 215 | def self.append_features(base) 216 | base.extend CallbackMethods 217 | base.extend self 218 | end 219 | 220 | # 221 | def method_undefined(method) 222 | if defined?(super) 223 | callback_invoke(:method_undefined, method){ super(method) } 224 | else 225 | callback_invoke(:method_undefined, method) 226 | end 227 | end 228 | end 229 | 230 | # Callback system for #method_added. 231 | module SingletonMethodAdded 232 | # 233 | def self.append_features(base) 234 | base.extend CallbackMethods 235 | base.extend self 236 | end 237 | 238 | # 239 | def singleton_method_added(method) 240 | if defined?(super) 241 | callback_invoke(:singleton_method_added, method){ super(method) } 242 | else 243 | callback_invoke(:singleton_method_added, method) 244 | end 245 | end 246 | end 247 | 248 | # Callback system for #singleton_method_removed. 249 | module SingletonMethodRemoved 250 | # 251 | def self.append_features(base) 252 | base.extend CallbackMethods 253 | base.extend self 254 | end 255 | 256 | # 257 | def singleton_method_removed(method) 258 | if defined?(super) 259 | callback_invoke(:singleton_method_removed, method){ super(method) } 260 | else 261 | callback_invoke(:singleton_method_removed, method) 262 | end 263 | end 264 | end 265 | 266 | # Callback system for #singleton_method_undefined. 267 | module SingletonMethodUndefined 268 | # 269 | def self.append_features(base) 270 | base.extend CallbackMethods 271 | base.extend self 272 | end 273 | 274 | # 275 | def singleton_method_undefined(method) 276 | if defined?(super) 277 | callback_invoke(:singleton_method_undefined, method){ super(method) } 278 | else 279 | callback_invoke(:singleton_method_undefined, method) 280 | end 281 | end 282 | end 283 | 284 | # Callback system for #included. 285 | module Included 286 | # 287 | def self.append_features(base) 288 | base.extend CallbackMethods 289 | base.extend self 290 | end 291 | 292 | # 293 | def included(mod) 294 | if defined?(super) 295 | callback_invoke(:included, mod){ super(mod) } 296 | else 297 | callback_invoke(:included, mod) 298 | end 299 | end 300 | end 301 | 302 | # Callback system for #extended. 303 | module Extended 304 | # 305 | def self.append_features(base) 306 | base.extend CallbackMethods 307 | base.extend self 308 | end 309 | 310 | # 311 | def extended(mod) 312 | if defined?(super) 313 | callback_invoke(:extended, mod){ super(mod) } 314 | else 315 | callback_invoke(:extended, mod) 316 | end 317 | end 318 | end 319 | 320 | # Callback system for #inherited. 321 | module Inherited 322 | # 323 | def self.append_features(base) 324 | base.extend CallbackMethods 325 | base.extend self 326 | end 327 | 328 | # 329 | def inherited(base) 330 | if defined?(super) 331 | callback_invoke(:inherited, base){ super(base) } 332 | else 333 | callback_invoke(:inherited, base) 334 | end 335 | end 336 | end 337 | 338 | # Callback system for #const_missing. 339 | # 340 | # Unlike other callback mixins, this does NOT invoke super (for obvious reasons). 341 | module ConstMissing 342 | # 343 | def self.append_features(base) 344 | base.extend CallbackMethods 345 | base.extend self 346 | end 347 | 348 | # 349 | def const_missing(const) 350 | if defined?(super) 351 | callback_invoke(:const_missing, const){ super(const) } 352 | else 353 | callback_invoke(:const_missing, const) 354 | end 355 | end 356 | end 357 | 358 | end 359 | --------------------------------------------------------------------------------