no_backsies 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +56 -0
- data/lib/no_backsies.rb +289 -0
- data/qed/01_example.rdoc +28 -0
- data/qed/02_express.rdoc +38 -0
- data/qed/applique/no_backsies.rb +2 -0
- metadata +105 -0
data/README.rdoc
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
= No Backsies
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
NoBackies is a callback layer built on top of Ruby's built-in callback
|
6
|
+
methods. It makes it possible to add new callbacks very easily, without
|
7
|
+
having to fuss with more nuanced issues of defining and redefining callback
|
8
|
+
methods.
|
9
|
+
|
10
|
+
|
11
|
+
== RESOURCES
|
12
|
+
|
13
|
+
* home: http://rubyworks.github.com/nobacksies
|
14
|
+
* code: http://github.com/rubyworks/nobacksies
|
15
|
+
* mail: http://groups.google.com/group/rubyworks-mailinglist
|
16
|
+
|
17
|
+
|
18
|
+
== EXAMPLE
|
19
|
+
|
20
|
+
Here is an example take from the Anise[http://rubyworks.github.com/anise]
|
21
|
+
project.
|
22
|
+
|
23
|
+
class Y
|
24
|
+
include Anise
|
25
|
+
include NoBacksies
|
26
|
+
|
27
|
+
def self.doc(string)
|
28
|
+
callback :method_added do |method|
|
29
|
+
self.ann(method, :doc=>string)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
doc "here"
|
34
|
+
|
35
|
+
def foo; end
|
36
|
+
end
|
37
|
+
|
38
|
+
Y.ann(:foo, :doc) #=> "here"
|
39
|
+
|
40
|
+
|
41
|
+
== INSTALLATION
|
42
|
+
|
43
|
+
Install the RubyGems package in the usual fahsion.
|
44
|
+
|
45
|
+
$ gem install nobacksies
|
46
|
+
|
47
|
+
|
48
|
+
== LEGAL
|
49
|
+
|
50
|
+
(Apache 2.0 License)
|
51
|
+
|
52
|
+
Copyright (c) 2011 Thomas Sawyer
|
53
|
+
|
54
|
+
Unless otherwise negotiated with the original author, NoBacksies is
|
55
|
+
distributable under the terms of the Apache 2.0 license. See the
|
56
|
+
Apache2.txt file for details.
|
data/lib/no_backsies.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
# NoBacksies module ecapsulates all supported callback mixins.
|
2
|
+
#
|
3
|
+
# NoBackseis::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
|
+
# NOTE: Currently the NoBackies module only supports class level callbacks.
|
50
|
+
# We will look into adding instance level callbacks in a future version.
|
51
|
+
#
|
52
|
+
#--
|
53
|
+
# TODO: What about adding `super if defined?(super)` to callback methods?
|
54
|
+
# Should this be standard? Should it occur before or after? Or should
|
55
|
+
# in be controlled via a special callback, e.g. `callback method_added, :super`?
|
56
|
+
#++
|
57
|
+
|
58
|
+
module NoBacksies
|
59
|
+
|
60
|
+
#
|
61
|
+
|
62
|
+
module Callbacks
|
63
|
+
# Apply all supported callback modules.
|
64
|
+
def self.append_features(base)
|
65
|
+
base.extend CallbackMethods
|
66
|
+
base.extend MethodAdded
|
67
|
+
base.extend MethodRemoved
|
68
|
+
base.extend MethodUndefined
|
69
|
+
base.extend SingletonMethodAdded
|
70
|
+
base.extend SingletonMethodRemoved
|
71
|
+
base.extend SingletonMethodUndefined
|
72
|
+
base.extend ConstMissing
|
73
|
+
base.extend Included
|
74
|
+
base.extend Extended
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# The CallbackMethods module adds the callback methods which are used
|
79
|
+
# define and access callback definitions. Mixing-in this module is
|
80
|
+
# handled automatically, so you do not need to worry with it. In other
|
81
|
+
# words, consider the module *private*.
|
82
|
+
|
83
|
+
module CallbackMethods
|
84
|
+
# Define a callback.
|
85
|
+
def callback(name, express={}, &block)
|
86
|
+
callbacks[name.to_sym] << block
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
def callbacks
|
91
|
+
@_callbacks ||= (
|
92
|
+
anc = ancestors[1..-1].find do |anc|
|
93
|
+
anc.callbacks rescue nil # TODO: Need faster way!
|
94
|
+
end
|
95
|
+
anc ? anc.callbacks.dup : Hash.new{|h,k| h[k]=[]}
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns Hash of true/false activity state of callbacks.
|
100
|
+
#
|
101
|
+
# TODO: Should expression be inherited?
|
102
|
+
def callback_express(express={}, &block)
|
103
|
+
@_callback_express ||= Hash.new{|h,k| h[k]=true}
|
104
|
+
|
105
|
+
if block
|
106
|
+
tmp = @_callback_express.dup
|
107
|
+
express.each{ |k,v| @_callback_express[k.to_sym] = !!v }
|
108
|
+
block.call
|
109
|
+
@_callback_express = tmp
|
110
|
+
else
|
111
|
+
express.each{ |k,v| @_callback_express[k.to_sym] = !!v }
|
112
|
+
end
|
113
|
+
|
114
|
+
@_callback_express
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Callback system for #method_added.
|
119
|
+
module MethodAdded
|
120
|
+
#
|
121
|
+
def self.append_features(base)
|
122
|
+
base.extend CallbackMethods
|
123
|
+
base.extend self
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
def method_added(method)
|
128
|
+
return unless callback_express[:method_added]
|
129
|
+
callbacks[:method_added].each do |block|
|
130
|
+
block.call(method)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Callback system for #method_removed.
|
136
|
+
module MethodRemoved
|
137
|
+
#
|
138
|
+
def self.append_features(base)
|
139
|
+
base.extend CallbackMethods
|
140
|
+
base.extend self
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
def method_removed(method)
|
145
|
+
return unless callback_express[:method_removed]
|
146
|
+
callbacks[:method_removed].each do |block|
|
147
|
+
block.call(method)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Callback system for #method_removed.
|
153
|
+
module MethodUndefined
|
154
|
+
#
|
155
|
+
def self.append_features(base)
|
156
|
+
base.extend CallbackMethods
|
157
|
+
base.extend self
|
158
|
+
end
|
159
|
+
|
160
|
+
#
|
161
|
+
def method_undefined(method)
|
162
|
+
return unless callback_express[:method_undefined]
|
163
|
+
callbacks[:method_undefined].each do |block|
|
164
|
+
block.call(method)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Callback system for #method_added.
|
170
|
+
module SingletonMethodAdded
|
171
|
+
#
|
172
|
+
def self.append_features(base)
|
173
|
+
base.extend CallbackMethods
|
174
|
+
base.extend self
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
def singleton_method_added(method)
|
179
|
+
return unless callback_express[:singleton_method_added]
|
180
|
+
callbacks[:singleton_method_added].each do |block|
|
181
|
+
block.call(method)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Callback system for #method_removed.
|
187
|
+
module SingletonMethodRemoved
|
188
|
+
#
|
189
|
+
def self.append_features(base)
|
190
|
+
base.extend CallbackMethods
|
191
|
+
base.extend self
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
def singleton_method_removed(method)
|
196
|
+
return unless callback_express[:singleton_method_removed]
|
197
|
+
callbacks[:singleton_method_removed].each do |block|
|
198
|
+
block.call(method)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Callback system for #method_removed.
|
204
|
+
module SingletonMethodUndefined
|
205
|
+
#
|
206
|
+
def self.append_features(base)
|
207
|
+
base.extend CallbackMethods
|
208
|
+
base.extend self
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
def singleton_method_undefined(method)
|
213
|
+
return unless callback_express[:singleton_method_undefined]
|
214
|
+
callbacks[:singleton_method_undefined].each do |block|
|
215
|
+
block.call(method)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Callback system for #const_missing.
|
221
|
+
module ConstMissing
|
222
|
+
#
|
223
|
+
def self.append_features(base)
|
224
|
+
base.extend CallbackMethods
|
225
|
+
base.extend self
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
def const_missing(const)
|
230
|
+
return unless callback_express[:cont_missing]
|
231
|
+
callbacks[:const_missing].each do |block|
|
232
|
+
block.call(const)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Callback system for #included.
|
238
|
+
module Included
|
239
|
+
#
|
240
|
+
def self.append_features(base)
|
241
|
+
base.extend CallbackMethods
|
242
|
+
base.extend self
|
243
|
+
end
|
244
|
+
|
245
|
+
#
|
246
|
+
def included(mod)
|
247
|
+
return unless callback_express[:included]
|
248
|
+
callbacks[:included].each do |block|
|
249
|
+
block.call(mod)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Callback system for #extended.
|
255
|
+
module Extended
|
256
|
+
#
|
257
|
+
def self.append_features(base)
|
258
|
+
base.extend CallbackMethods
|
259
|
+
base.extend self
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
def extended(mod)
|
264
|
+
return unless callback_express[:extended]
|
265
|
+
callbacks[:extended].each do |block|
|
266
|
+
block.call(mod)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Callback system for #inherited.
|
272
|
+
module Inherited
|
273
|
+
#
|
274
|
+
def self.append_features(base)
|
275
|
+
base.extend CallbackMethods
|
276
|
+
base.extend self
|
277
|
+
end
|
278
|
+
|
279
|
+
#
|
280
|
+
def extended(mod)
|
281
|
+
return unless callback_express[:inherited]
|
282
|
+
callbacks[:inherited].each do |block|
|
283
|
+
block.call(mod)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
|
data/qed/01_example.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
+
|
data/qed/02_express.rdoc
ADDED
@@ -0,0 +1,38 @@
|
|
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
|
+
def self.list
|
15
|
+
@list ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
callback :method_added do |method|
|
19
|
+
callback_express :method_added=>false do
|
20
|
+
define_method("#{method}!") do
|
21
|
+
send(method) + "!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def foo; "foo"; end
|
27
|
+
def bar; "bar"; end
|
28
|
+
end
|
29
|
+
|
30
|
+
In this example, a new `Z` object will get an automatically defined bang method
|
31
|
+
for every explicitly defined method.
|
32
|
+
|
33
|
+
z = Z.new
|
34
|
+
z.foo #=> "foo"
|
35
|
+
z.foo! #=> "foo!"
|
36
|
+
z.bar #=> "bar"
|
37
|
+
z.bar! #=> "bar!"
|
38
|
+
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: no_backsies
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Thomas Sawyer
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-29 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: syckle
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: qed
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: |-
|
50
|
+
NoBackies is a callback layer built on top of Ruby's built-in callback
|
51
|
+
methods. It makes it possible to add new callbacks very easily, without
|
52
|
+
having to fuss with more nuanced issues of defining and redefining callback
|
53
|
+
methods.
|
54
|
+
email: transfire@gmail.com
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files:
|
60
|
+
- README.rdoc
|
61
|
+
files:
|
62
|
+
- lib/no_backsies.rb
|
63
|
+
- qed/01_example.rdoc
|
64
|
+
- qed/02_express.rdoc
|
65
|
+
- qed/applique/no_backsies.rb
|
66
|
+
- README.rdoc
|
67
|
+
has_rdoc: true
|
68
|
+
homepage: http://rubyworks.github.com/nobacksies
|
69
|
+
licenses: []
|
70
|
+
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options:
|
73
|
+
- --title
|
74
|
+
- NoBacksies API
|
75
|
+
- --main
|
76
|
+
- README.rdoc
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 3
|
85
|
+
segments:
|
86
|
+
- 0
|
87
|
+
version: "0"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
requirements: []
|
98
|
+
|
99
|
+
rubyforge_project: no_backsies
|
100
|
+
rubygems_version: 1.3.7
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Better handling of Ruby callbacks
|
104
|
+
test_files: []
|
105
|
+
|