facets 2.1.0 → 2.1.1
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/lib/core/facets/array/indexable.rb +36 -0
- data/lib/core/facets/kernel.rb +2 -0
- data/lib/core/facets/kernel/ergo.rb +40 -0
- data/lib/core/facets/kernel/tap.rb +4 -6
- data/lib/core/facets/nilclass.rb +0 -1
- data/lib/core/facets/string/case.rb +11 -45
- data/lib/methods/facets/array/each_iteration.rb +1 -0
- data/lib/methods/facets/nilclass/ergo.rb +1 -0
- data/lib/methods/facets/string/titlecase.rb +1 -0
- data/lib/more/facets/command.rb +12 -2
- data/lib/more/facets/logger.rb +152 -14
- data/lib/more/facets/validation.rb +445 -0
- data/log/history.txt +5 -0
- data/meta/{facets-2.1.0.roll → facets-2.1.1.roll} +0 -0
- data/meta/manifest.txt +8 -3
- data/task/methods +2 -0
- data/task/svn/tag +22 -0
- metadata +11 -6
- data/lib/core/facets/nilclass/ergo.rb +0 -13
- data/lib/more/facets/tracepoint.rb +0 -194
@@ -28,6 +28,42 @@ class Array
|
|
28
28
|
# p a #=> [1,2,3]
|
29
29
|
#
|
30
30
|
alias_method :last!, :pop
|
31
|
+
|
32
|
+
# Iteration object.
|
33
|
+
|
34
|
+
class It
|
35
|
+
attr_reader :index, :value, :prior, :after
|
36
|
+
def initialize(array)
|
37
|
+
@array = array
|
38
|
+
@index = 0
|
39
|
+
@value = array[0]
|
40
|
+
@prior = []
|
41
|
+
@after = array[1..-1]
|
42
|
+
end
|
43
|
+
def first? ; @index == 0 ; end
|
44
|
+
def last? ; @index == @array.length ; end
|
45
|
+
private
|
46
|
+
def next_iteration
|
47
|
+
@index += 1
|
48
|
+
@prior << @value
|
49
|
+
@value = @after.shift
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Iterate over each element of array using an iteration object.
|
54
|
+
|
55
|
+
def each_iteration
|
56
|
+
if block_given?
|
57
|
+
it = It.new(self)
|
58
|
+
each do |e|
|
59
|
+
yield(it)
|
60
|
+
it.send(:next_iteration)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
return Enumerable::Enumerator.new(self, :each_iteration)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
31
67
|
end
|
32
68
|
|
33
69
|
|
data/lib/core/facets/kernel.rb
CHANGED
@@ -5,6 +5,7 @@ require 'facets/kernel/val.rb'
|
|
5
5
|
require 'facets/kernel/metaid.rb'
|
6
6
|
require 'facets/kernel/instance.rb'
|
7
7
|
require 'facets/kernel/tap.rb'
|
8
|
+
require 'facets/kernel/ergo.rb'
|
8
9
|
require 'facets/kernel/object.rb'
|
9
10
|
require 'facets/kernel/withattr.rb'
|
10
11
|
require 'facets/kernel/deepcopy.rb'
|
@@ -17,3 +18,4 @@ require 'facets/kernel/ask.rb'
|
|
17
18
|
require 'facets/kernel/respond.rb'
|
18
19
|
require 'facets/kernel/silence.rb'
|
19
20
|
require 'facets/kernel/report.rb'
|
21
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'facets/functor'
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
|
5
|
+
# Yield self -or- return self.
|
6
|
+
#
|
7
|
+
# "a".ergo.upcase #=> "A"
|
8
|
+
# nil.ergo.foobar #=> nil
|
9
|
+
#
|
10
|
+
# "a".ergo{ |o| o.upcase } #=> "A"
|
11
|
+
# nil.ergo{ |o| o.foobar } #=> nil
|
12
|
+
#
|
13
|
+
# This is like #tap, but tap yields self -and- returns self.
|
14
|
+
#
|
15
|
+
# CREDIT Daniel DeLorme
|
16
|
+
|
17
|
+
def ergo &b
|
18
|
+
if block_given?
|
19
|
+
b.arity == 1 ? yield(self) : instance_eval(&b)
|
20
|
+
else
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class NilClass
|
28
|
+
|
29
|
+
# Compliments Kernel#ergo.
|
30
|
+
#
|
31
|
+
# "a".ergo{ |o| o.upcase } #=> "A"
|
32
|
+
# nil.ergo{ |o| o.bar } #=> nil
|
33
|
+
#
|
34
|
+
# CREDIT Daniel DeLorme
|
35
|
+
|
36
|
+
def ergo
|
37
|
+
@_ergo ||= Functor.new{ nil }
|
38
|
+
@_ergo unless block_given?
|
39
|
+
end
|
40
|
+
end
|
@@ -15,13 +15,11 @@
|
|
15
15
|
#
|
16
16
|
module Kernel
|
17
17
|
|
18
|
-
# Yield self and return self.
|
18
|
+
# Yield self -and- return self.
|
19
19
|
|
20
|
-
def tap
|
21
|
-
if
|
22
|
-
yield
|
23
|
-
else
|
24
|
-
instance_eval(&b)
|
20
|
+
def tap(&b)
|
21
|
+
if block_given?
|
22
|
+
b.arity == 1 ? yield(self) : instance_eval(&b)
|
25
23
|
end
|
26
24
|
self
|
27
25
|
end
|
data/lib/core/facets/nilclass.rb
CHANGED
@@ -18,37 +18,45 @@
|
|
18
18
|
#
|
19
19
|
class String
|
20
20
|
|
21
|
-
#
|
21
|
+
# Title case.
|
22
|
+
|
23
|
+
def titlecase
|
24
|
+
gsub(/\b\w/){$&.upcase}
|
25
|
+
end
|
22
26
|
|
23
27
|
# Return true if the string is capitalized, otherwise false.
|
24
28
|
#
|
25
29
|
# "THIS".capitalized? #=> true
|
26
30
|
# "This".capitalized? #=> true
|
27
31
|
# "this".capitalized? #=> false
|
32
|
+
#
|
33
|
+
# CREDIT: Phil Tomson
|
28
34
|
|
29
35
|
def capitalized?
|
30
36
|
self =~ /^[A-Z]/
|
31
37
|
end
|
32
38
|
|
33
|
-
# CREDIT Phil Tomson
|
34
39
|
|
35
40
|
# Return true if the string is lowercase (downcase), otherwise false.
|
36
41
|
#
|
37
42
|
# "THIS".downcase? #=> false
|
38
43
|
# "This".downcase? #=> false
|
39
44
|
# "this".downcase? #=> true
|
45
|
+
#
|
46
|
+
# CREDIT Phil Tomson
|
40
47
|
|
41
48
|
def downcase?
|
42
49
|
downcase == self
|
43
50
|
end
|
44
51
|
|
45
|
-
# CREDIT Phil Tomson
|
46
52
|
|
47
53
|
# Is the string upcase/uppercase?
|
48
54
|
#
|
49
55
|
# "THIS".upcase? #=> true
|
50
56
|
# "This".upcase? #=> false
|
51
57
|
# "this".upcase? #=> false
|
58
|
+
#
|
59
|
+
# CREDIT Phil Tomson
|
52
60
|
|
53
61
|
def upcase?
|
54
62
|
self.upcase == self
|
@@ -61,45 +69,3 @@ class String
|
|
61
69
|
alias_method :uppercase?, :upcase?
|
62
70
|
end
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
# _____ _
|
67
|
-
# |_ _|__ ___| |_
|
68
|
-
# | |/ _ \/ __| __|
|
69
|
-
# | | __/\__ \ |_
|
70
|
-
# |_|\___||___/\__|
|
71
|
-
#
|
72
|
-
=begin test
|
73
|
-
|
74
|
-
require 'test/unit'
|
75
|
-
|
76
|
-
#class String
|
77
|
-
# include Compare
|
78
|
-
#end
|
79
|
-
|
80
|
-
class TestStringCase < Test::Unit::TestCase
|
81
|
-
|
82
|
-
def test_capitalized?
|
83
|
-
assert( 'Abc'.capitalized? )
|
84
|
-
end
|
85
|
-
|
86
|
-
def test_downcase?
|
87
|
-
assert( 'abc'.downcase? )
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_lowercase?
|
91
|
-
assert( 'abc'.lowercase? )
|
92
|
-
end
|
93
|
-
|
94
|
-
def test_upcase?
|
95
|
-
assert( 'ABC'.upcase? )
|
96
|
-
end
|
97
|
-
|
98
|
-
def test_uppercase?
|
99
|
-
assert( 'ABC'.uppercase? )
|
100
|
-
end
|
101
|
-
|
102
|
-
end
|
103
|
-
|
104
|
-
=end
|
105
|
-
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/array/indexable.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/kernel/ergo.rb'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'facets/string/case.rb'
|
data/lib/more/facets/command.rb
CHANGED
@@ -108,6 +108,7 @@ module Console
|
|
108
108
|
pre = cargs.preoptions
|
109
109
|
cmd, argv = *cargs.subcommand
|
110
110
|
args, opts = *argv
|
111
|
+
|
111
112
|
if is_a?(UniversalOptions)
|
112
113
|
new(pre, opts).call(cmd, args, opts)
|
113
114
|
else
|
@@ -145,6 +146,7 @@ module Console
|
|
145
146
|
#
|
146
147
|
|
147
148
|
def initialize(*options)
|
149
|
+
@master_options = {}
|
148
150
|
initialize_options(*options)
|
149
151
|
end
|
150
152
|
|
@@ -155,12 +157,13 @@ module Console
|
|
155
157
|
begin
|
156
158
|
opt, val = nil, nil
|
157
159
|
options.each do |opt, val|
|
160
|
+
opt = opt.gsub('-','_')
|
158
161
|
send("#{opt}=", val)
|
159
162
|
end
|
160
163
|
rescue NoMethodError
|
161
164
|
option_missing(opt, val)
|
162
165
|
end
|
163
|
-
@master_options
|
166
|
+
@master_options.update(options)
|
164
167
|
end
|
165
168
|
|
166
169
|
public
|
@@ -212,8 +215,15 @@ module Console
|
|
212
215
|
|
213
216
|
class Command
|
214
217
|
|
218
|
+
def self.option_arity(arity_hash=nil)
|
219
|
+
if arity_hash
|
220
|
+
(@option_arity ||= {}).merge!(arity_hash)
|
221
|
+
end
|
222
|
+
@option_arity
|
223
|
+
end
|
224
|
+
|
215
225
|
def self.start(line=nil)
|
216
|
-
cargs = Console::Argument.new(line || ARGV)
|
226
|
+
cargs = Console::Argument.new(line || ARGV, option_arity)
|
217
227
|
pre = cargs.preoptions
|
218
228
|
args, opts = *cargs.parameters
|
219
229
|
new(args, opts).call
|
data/lib/more/facets/logger.rb
CHANGED
@@ -24,37 +24,175 @@
|
|
24
24
|
# AUTHORS:
|
25
25
|
#
|
26
26
|
# - George Moschovitis
|
27
|
+
# - TransLogathon
|
27
28
|
|
28
|
-
|
29
|
-
require
|
29
|
+
require "logger"
|
30
|
+
require "time"
|
31
|
+
require "facets/ansicode"
|
30
32
|
|
31
33
|
# = Logger
|
32
34
|
#
|
33
35
|
# Extended variation of Ruby's standard Logger library.
|
36
|
+
# Mainly for compatibility purposes (with what?)
|
37
|
+
#
|
38
|
+
# log = Logger.new
|
39
|
+
#
|
40
|
+
# log.setup_format do |severity, timestamp, progname, msg|
|
41
|
+
# Logger::SIMPLE_FORMAT % [severity, msg]
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# === Convention
|
45
|
+
#
|
46
|
+
# When using debug level logger messages always append 'if $DBG'
|
47
|
+
# at the end. This hack is needed because Ruby does not support
|
48
|
+
# lazy evaluation (lisp macros).
|
34
49
|
#
|
50
|
+
# TODO What's all this about then?
|
51
|
+
|
35
52
|
class Logger
|
53
|
+
# Some available logging formats.
|
54
|
+
SIMPLE_FORMAT = "%5s: %s\n"
|
55
|
+
DETAILED_FORMAT = "%s %5s: %s\n"
|
56
|
+
|
57
|
+
# Why these names ?
|
58
|
+
alias_method :devel, :debug
|
59
|
+
alias_method :fine, :debug
|
60
|
+
|
61
|
+
# Prints a trace message to DEBUGLOG (at debug level).
|
62
|
+
# Useful for emitting the value of variables, etc. Use
|
63
|
+
# like this:
|
64
|
+
#
|
65
|
+
# x = y = 5
|
66
|
+
# trace 'x' # -> 'x = 5'
|
67
|
+
# trace 'x ** y' # -> 'x ** y = 3125'
|
68
|
+
#
|
69
|
+
# If you have a more complicated value, like an array of
|
70
|
+
# hashes, then you'll probably want to use an alternative
|
71
|
+
# output format. For instance:
|
72
|
+
#
|
73
|
+
# trace 'value', :yaml
|
74
|
+
#
|
75
|
+
# Valid output format values (the _style_ parameter) are:
|
76
|
+
#
|
77
|
+
# :p :inspect
|
78
|
+
# :pp (pretty-print, using 'pp' library)
|
79
|
+
# :s :to_s
|
80
|
+
# :y :yaml :to_yaml (using the 'yaml' library')
|
81
|
+
#
|
82
|
+
# The default is <tt>:p</tt>.
|
83
|
+
#
|
84
|
+
# CREDITS:
|
85
|
+
#
|
86
|
+
# This code comes straight from the dev-utils Gem.
|
87
|
+
# Author: Gavin Sinclair <gsinclair@soyabean.com.au>
|
88
|
+
|
89
|
+
def trace(expr, style=:p)
|
90
|
+
unless expr.respond_to? :to_str
|
91
|
+
warn "trace: Can't evaluate the given value: #{caller.first}"
|
92
|
+
else
|
93
|
+
raise "FACETS: binding/or_caller is no longer possible"
|
94
|
+
require "facets/core/binding/self/of_caller"
|
95
|
+
|
96
|
+
Binding.of_caller do |b|
|
97
|
+
value = b.eval(expr.to_str)
|
98
|
+
formatter = TRACE_STYLES[style] || :inspect
|
99
|
+
case formatter
|
100
|
+
when :pp then require 'pp'
|
101
|
+
when :y, :yaml, :to_yaml then require 'yaml'
|
102
|
+
end
|
103
|
+
value_s = value.send(formatter)
|
104
|
+
message = "#{expr} = #{value_s}"
|
105
|
+
lines = message.split(/\n/)
|
106
|
+
indent = " "
|
107
|
+
debug(lines.shift)
|
108
|
+
lines.each do |line|
|
109
|
+
debug(indent + line)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
36
114
|
|
37
|
-
|
115
|
+
TRACE_STYLES = {} # :nodoc:
|
116
|
+
TRACE_STYLES.update(
|
117
|
+
:pp => :pp_s, :s => :to_s, :p => :inspect,
|
118
|
+
:y => :to_yaml, :yaml => :to_yaml,
|
119
|
+
:inspect => :inspect, :to_yaml => :to_yaml
|
120
|
+
)
|
38
121
|
|
39
|
-
# Dictate the way in which this logger should format the
|
40
|
-
# it displays. This method requires a block. The
|
41
|
-
# formatted strings given severity,
|
122
|
+
# Dictate the way in which this logger should format the
|
123
|
+
# messages it displays. This method requires a block. The
|
124
|
+
# block should return formatted strings given severity,
|
125
|
+
# timestamp, msg, progname.
|
42
126
|
#
|
43
|
-
#
|
127
|
+
# === Example
|
44
128
|
#
|
45
129
|
# logger = Logger.new
|
46
|
-
# logger.
|
130
|
+
# logger.setup_format do |severity, timestamp, msg, progname|
|
47
131
|
# "#{progname}@#{timestamp} - #{severity}::#{msg}"
|
48
132
|
# end
|
49
|
-
|
50
|
-
def
|
51
|
-
raise
|
133
|
+
|
134
|
+
def setup_format(&format_proc)
|
135
|
+
raise "Formating block needed" unless format_proc
|
52
136
|
@format_proc = format_proc
|
53
137
|
end
|
54
138
|
|
55
|
-
|
56
|
-
|
57
|
-
|
139
|
+
private
|
140
|
+
|
141
|
+
attr_accessor :format_proc
|
142
|
+
|
143
|
+
alias_method :format_message_without_proc, :format_message # :nodoc:
|
144
|
+
|
145
|
+
def format_message(*args) # :nodoc:
|
146
|
+
@format_proc ? @format_proc.call(*args) : format_message_without_proc(*args)
|
58
147
|
end
|
59
148
|
|
60
149
|
end
|
150
|
+
|
151
|
+
|
152
|
+
module Console
|
153
|
+
|
154
|
+
class Logger < ::Logger
|
155
|
+
|
156
|
+
attr_accessor :style
|
157
|
+
|
158
|
+
def style
|
159
|
+
@style ||= Style.new
|
160
|
+
end
|
161
|
+
|
162
|
+
def info(str)
|
163
|
+
return if level > Logger::INFO
|
164
|
+
self << style.info(str)
|
165
|
+
super
|
166
|
+
self << Console::ANSICode.clear
|
167
|
+
end
|
168
|
+
|
169
|
+
def warn(str)
|
170
|
+
self << style.warn(str)
|
171
|
+
super
|
172
|
+
self << Console::ANSICode.clear
|
173
|
+
end
|
174
|
+
|
175
|
+
def error(str)
|
176
|
+
return if level > Logger::ERROR
|
177
|
+
self << style.error(str)
|
178
|
+
super
|
179
|
+
self << Console::ANSICode.clear
|
180
|
+
end
|
181
|
+
|
182
|
+
def debug(str)
|
183
|
+
self << style.debug(str)
|
184
|
+
super
|
185
|
+
self << Console::ANSICode.clear
|
186
|
+
end
|
187
|
+
|
188
|
+
# Logger Style
|
189
|
+
|
190
|
+
class Style
|
191
|
+
def info(str) ; ANSICode.green ; end
|
192
|
+
def warn(str) ; ANSICode.cyan ; end
|
193
|
+
def error(str) ; ANSICode.bold + ANSICode.red ; end
|
194
|
+
def debug(str) ; '' ; end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,445 @@
|
|
1
|
+
require 'facets/class/cattr'
|
2
|
+
require 'facets/module/on_included'
|
3
|
+
require 'facets/inheritor'
|
4
|
+
require 'facets/settings'
|
5
|
+
|
6
|
+
module Glue
|
7
|
+
|
8
|
+
# Implements a meta-language for validating managed
|
9
|
+
# objects. Typically used in Validator objects but can be
|
10
|
+
# included in managed objects too.
|
11
|
+
#
|
12
|
+
# Additional og-related validation macros can be found
|
13
|
+
# in lib/og/validation.
|
14
|
+
#
|
15
|
+
# The following validation macros are available:
|
16
|
+
#
|
17
|
+
# * validate_value
|
18
|
+
# * validate_confirmation
|
19
|
+
# * validate_format
|
20
|
+
# * validate_length
|
21
|
+
# * validate_inclusion
|
22
|
+
#
|
23
|
+
# Og/Database specific validation methods are added in the
|
24
|
+
# file og/validation.rb
|
25
|
+
#
|
26
|
+
# === Example
|
27
|
+
#
|
28
|
+
# class User
|
29
|
+
# attr_accessor :name, String
|
30
|
+
# attr_accessor :level, Fixnum
|
31
|
+
#
|
32
|
+
# validate_length :name, :range => 2..6
|
33
|
+
# validate_unique :name, :msg => :name_allready_exists
|
34
|
+
# validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# class CustomUserValidator
|
38
|
+
# include Validation
|
39
|
+
# validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# user = @request.fill(User.new)
|
43
|
+
# user.level = 15
|
44
|
+
#
|
45
|
+
# unless user.valid?
|
46
|
+
# user.save
|
47
|
+
# else
|
48
|
+
# p user.errors[:name]
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# unless user.save
|
52
|
+
# p user.errors.on(:name)
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# unless errors = CustomUserValidator.errors(user)
|
56
|
+
# user.save
|
57
|
+
# else
|
58
|
+
# p errors[:name]
|
59
|
+
# end
|
60
|
+
#--
|
61
|
+
# TODO: all validation methods should imply validate_value.
|
62
|
+
#++
|
63
|
+
|
64
|
+
module Validation
|
65
|
+
|
66
|
+
# The postfix attached to confirmation attributes.
|
67
|
+
|
68
|
+
setting :confirmation_postfix, :default => '_confirmation', :doc => 'The postfix attached to confirmation attributes.'
|
69
|
+
|
70
|
+
# Encapsulates a list of validation errors.
|
71
|
+
|
72
|
+
class Errors
|
73
|
+
attr_accessor :errors
|
74
|
+
|
75
|
+
setting :no_value, :default => 'No value provided'
|
76
|
+
setting :no_confirmation, :default => 'Invalid confirmation'
|
77
|
+
setting :invalid_format, :default => 'Invalid format'
|
78
|
+
setting :too_short, :default => 'Too short, must be more than %d characters long'
|
79
|
+
setting :too_long, :default => 'Too long, must be less than %d characters long'
|
80
|
+
setting :invalid_length, :default => 'Must be %d characters long'
|
81
|
+
setting :no_inclusion, :default => 'The value is invalid'
|
82
|
+
setting :no_numeric, :default => 'The value must be numeric'
|
83
|
+
setting :not_unique, :default => 'The value is already used'
|
84
|
+
|
85
|
+
def initialize(errors = {})
|
86
|
+
@errors = errors
|
87
|
+
end
|
88
|
+
|
89
|
+
def add(attr, message)
|
90
|
+
(@errors[attr] ||= []) << message
|
91
|
+
end
|
92
|
+
|
93
|
+
def on(attr)
|
94
|
+
@errors[attr]
|
95
|
+
end
|
96
|
+
alias_method :[], :on
|
97
|
+
|
98
|
+
# Yields each attribute and associated message.
|
99
|
+
|
100
|
+
def each
|
101
|
+
@errors.each_key do |attr|
|
102
|
+
@errors[attr].each { |msg| yield attr, msg }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def size
|
107
|
+
@errors.size
|
108
|
+
end
|
109
|
+
alias_method :count, :size
|
110
|
+
|
111
|
+
def empty?
|
112
|
+
@errors.empty?
|
113
|
+
end
|
114
|
+
|
115
|
+
def clear
|
116
|
+
@errors.clear
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_a
|
120
|
+
@errors.inject([]) { |a, kv| a << kv }
|
121
|
+
end
|
122
|
+
|
123
|
+
def join(glue)
|
124
|
+
@errors.to_a.join(glue)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# A Key is used to uniquely identify a validation rule.
|
129
|
+
|
130
|
+
class Key
|
131
|
+
attr_reader :validation
|
132
|
+
attr_reader :field
|
133
|
+
|
134
|
+
def initialize(validation, field)
|
135
|
+
@validation, @field = validation.to_s, field.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
def hash
|
139
|
+
"#{@validation}-#{@field}".hash
|
140
|
+
end
|
141
|
+
|
142
|
+
def ==(other)
|
143
|
+
self.validation == other.validation and self.field == other.field
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# If the validate method returns true, this
|
148
|
+
# attributes holds the errors found.
|
149
|
+
|
150
|
+
attr_accessor :errors
|
151
|
+
|
152
|
+
# Call the #validate method for this object.
|
153
|
+
# If validation errors are found, sets the
|
154
|
+
# @errors attribute to the Errors object and
|
155
|
+
# returns true.
|
156
|
+
|
157
|
+
def valid?
|
158
|
+
validate
|
159
|
+
@errors.empty?
|
160
|
+
end
|
161
|
+
|
162
|
+
# Evaluate the class and see if it is valid.
|
163
|
+
# Can accept any parameter for 'on' event,
|
164
|
+
# and defaults to :save
|
165
|
+
|
166
|
+
def validate(on = :save)
|
167
|
+
@errors = Errors.new
|
168
|
+
|
169
|
+
return if self.class.validations.length == 0
|
170
|
+
|
171
|
+
for event, block in self.class.validations
|
172
|
+
block.call(self) if event == on.to_sym
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
on_included %{
|
177
|
+
base.module_eval do
|
178
|
+
inheritor(:validations, [], :+) unless @validations
|
179
|
+
# THINK: investigate carefuly!
|
180
|
+
def self.included(cbase)
|
181
|
+
super
|
182
|
+
cbase.send :include, Glue::Validation
|
183
|
+
end
|
184
|
+
end
|
185
|
+
base.extend(ClassMethods)
|
186
|
+
}
|
187
|
+
|
188
|
+
# Implements the Validation meta-language.
|
189
|
+
|
190
|
+
module ClassMethods
|
191
|
+
# Validates that the attributes have a values, ie they are
|
192
|
+
# neither nil or empty.
|
193
|
+
#
|
194
|
+
# === Example
|
195
|
+
#
|
196
|
+
# validate_value :param, :msg => 'No confirmation'
|
197
|
+
|
198
|
+
def validate_value(*params)
|
199
|
+
c = parse_config(params, :msg => Glue::Validation::Errors.no_value, :on => :save)
|
200
|
+
|
201
|
+
params.each do |field|
|
202
|
+
define_validation(:value, field, c[:on]) do |obj|
|
203
|
+
value = obj.send(field)
|
204
|
+
obj.errors.add(field, c[:msg]) if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
=begin
|
209
|
+
# Validates the confirmation of +String+ attributes.
|
210
|
+
#
|
211
|
+
# === Example
|
212
|
+
#
|
213
|
+
# validate_confirmation :password, :msg => 'No confirmation'
|
214
|
+
|
215
|
+
def validate_confirmation(*params)
|
216
|
+
c = parse_config(params,
|
217
|
+
:msg => Glue::Validation::Errors.no_confirmation,
|
218
|
+
:postfix => Glue::Validation.confirmation_postfix,
|
219
|
+
:on => :save
|
220
|
+
)
|
221
|
+
|
222
|
+
params.each do |field|
|
223
|
+
confirm_name = field.to_s + c[:postfix]
|
224
|
+
attr_accessor confirm_name.to_sym
|
225
|
+
|
226
|
+
define_validation(:confirmation, field, c[:on]) do |obj|
|
227
|
+
value = obj.send(field)
|
228
|
+
obj.errors.add(field, c[:msg]) if value.nil? or obj.send(confirm_name) != value
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
=end
|
233
|
+
# Validates the format of +String+ attributes.
|
234
|
+
# WARNING: regexp options are ignored.
|
235
|
+
#
|
236
|
+
# === Example
|
237
|
+
#
|
238
|
+
# validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
|
239
|
+
#--
|
240
|
+
# FIXME: how to get the Regexp options?
|
241
|
+
#++
|
242
|
+
|
243
|
+
def validate_format(*params)
|
244
|
+
c = parse_config(params,
|
245
|
+
:format => nil,
|
246
|
+
:msg_no_value => Glue::Validation::Errors.no_value,
|
247
|
+
:msg => Glue::Validation::Errors.invalid_format,
|
248
|
+
:on => :save
|
249
|
+
)
|
250
|
+
|
251
|
+
unless c[:format].is_a?(Regexp)
|
252
|
+
raise ArgumentError, "A regular expression must be supplied as the :format option for validate_format."
|
253
|
+
end
|
254
|
+
|
255
|
+
params.each do |field|
|
256
|
+
define_validation(:format, field, c[:on]) do |obj|
|
257
|
+
value = obj.send(field)
|
258
|
+
obj.errors.add(field, c[:msg]) if value.to_s !~ c[:format]
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Validates the length of +String+ attributes.
|
264
|
+
#
|
265
|
+
# === Example
|
266
|
+
#
|
267
|
+
# validate_length :name, :max => 30, :msg => 'Too long'
|
268
|
+
# validate_length :name, :min => 2, :msg => 'Too sort'
|
269
|
+
# validate_length :name, :range => 2..30
|
270
|
+
# validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
|
271
|
+
|
272
|
+
LENGTHS = [:min, :max, :range, :length].freeze
|
273
|
+
|
274
|
+
def validate_length(*params)
|
275
|
+
c = parse_config(params,
|
276
|
+
# :lengths => {:min => nil, :max => nil, :range => nil, :length => nil},
|
277
|
+
:min => nil, :max => nil, :range => nil, :length => nil,
|
278
|
+
:msg => nil,
|
279
|
+
:msg_no_value => Glue::Validation::Errors.no_value,
|
280
|
+
:msg_short => Glue::Validation::Errors.too_short,
|
281
|
+
:msg_long => Glue::Validation::Errors.too_long,
|
282
|
+
:msg_invalid => Glue::Validation::Errors.invalid_length,
|
283
|
+
:on => :save
|
284
|
+
)
|
285
|
+
|
286
|
+
length_params = c.reject {|k,v| !LENGTHS.include?(k) || v.nil? }
|
287
|
+
valid_count = length_params.reject{|k,v| v.nil?}.length
|
288
|
+
|
289
|
+
if valid_count == 0
|
290
|
+
raise ArgumentError, 'One of :min, :max, :range, or :length must be provided!'
|
291
|
+
elsif valid_count > 1
|
292
|
+
raise ArgumentError, 'You can only use one of :min, :max, :range, or :length options!'
|
293
|
+
end
|
294
|
+
|
295
|
+
operation, required = length_params.keys[0], length_params.values[0]
|
296
|
+
|
297
|
+
params.each do |field|
|
298
|
+
define_validation(:length, field, c[:on]) do |obj|
|
299
|
+
msg = c[:msg]
|
300
|
+
value = obj.send(field)
|
301
|
+
if value.nil?
|
302
|
+
obj.errors.add(field, c[:msg_no_value])
|
303
|
+
else
|
304
|
+
case operation
|
305
|
+
when :min
|
306
|
+
msg ||= c[:msg_short]
|
307
|
+
obj.errors.add(field, msg % required) if value.length < required
|
308
|
+
when :max
|
309
|
+
msg ||= c[:msg_long]
|
310
|
+
obj.errors.add(field, msg % required) if value.length > required
|
311
|
+
when :range
|
312
|
+
min, max = required.first, required.last
|
313
|
+
if value.length < min
|
314
|
+
msg ||= c[:msg_short]
|
315
|
+
obj.errors.add(field, msg % min)
|
316
|
+
end
|
317
|
+
if value.length > max
|
318
|
+
msg ||= c[:msg_long]
|
319
|
+
obj.errors.add(field, msg % min)
|
320
|
+
end
|
321
|
+
when :length
|
322
|
+
msg ||= c[:msg_invalid]
|
323
|
+
obj.errors.add(field, msg % required) if value.length != required
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Validates that the attributes are included in
|
331
|
+
# an enumeration.
|
332
|
+
#
|
333
|
+
# === Example
|
334
|
+
#
|
335
|
+
# validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
|
336
|
+
# validate_inclusion :age, :in => 5..99
|
337
|
+
|
338
|
+
def validate_inclusion(*params)
|
339
|
+
c = parse_config(params,
|
340
|
+
:in => nil,
|
341
|
+
:msg => Glue::Validation::Errors.no_inclusion,
|
342
|
+
:allow_nil => false,
|
343
|
+
:on => :save
|
344
|
+
)
|
345
|
+
|
346
|
+
unless c[:in].respond_to?('include?')
|
347
|
+
raise(ArgumentError, 'An object that responds to #include? must be supplied as the :in option')
|
348
|
+
end
|
349
|
+
|
350
|
+
params.each do |field|
|
351
|
+
define_validation(:inclusion, field, c[:on]) do |obj|
|
352
|
+
value = obj.send(field)
|
353
|
+
unless (c[:allow_nil] && value.nil?) or c[:in].include?(value)
|
354
|
+
obj.errors.add(field, c[:msg])
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Validates that the attributes have numeric values.
|
361
|
+
#
|
362
|
+
# [+:integer+]
|
363
|
+
# Only accept integers
|
364
|
+
#
|
365
|
+
# [+:msg]
|
366
|
+
# Custom message
|
367
|
+
#
|
368
|
+
# [+:on:]
|
369
|
+
# When to validate
|
370
|
+
#
|
371
|
+
# === Example
|
372
|
+
#
|
373
|
+
# validate_numeric :age, :msg => 'Must be an integer'
|
374
|
+
|
375
|
+
def validate_numeric(*params)
|
376
|
+
c = parse_config(params,
|
377
|
+
:integer => false,
|
378
|
+
:msg => Glue::Validation::Errors.no_numeric,
|
379
|
+
:on => :save
|
380
|
+
)
|
381
|
+
|
382
|
+
params.each do |field|
|
383
|
+
define_validation(:numeric, field, c[:on]) do |obj|
|
384
|
+
value = obj.send(field).to_s
|
385
|
+
if c[:integer]
|
386
|
+
unless value =~ /^[\+\-]*\d+$/
|
387
|
+
obj.errors.add(field, c[:msg])
|
388
|
+
end
|
389
|
+
else
|
390
|
+
begin
|
391
|
+
Float(value)
|
392
|
+
rescue ArgumentError, TypeError
|
393
|
+
obj.errors.add(field, c[:msg])
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
alias_method :validate_numericality, :validate_numeric
|
400
|
+
|
401
|
+
protected
|
402
|
+
|
403
|
+
# Parse the configuration for a validation by comparing
|
404
|
+
# the default options with the user-specified options,
|
405
|
+
# and returning the results.
|
406
|
+
|
407
|
+
def parse_config(params, defaults = {})
|
408
|
+
defaults.update(params.pop) if params.last.is_a?(Hash)
|
409
|
+
defaults
|
410
|
+
end
|
411
|
+
|
412
|
+
# Define a validation for this class on the specified event.
|
413
|
+
# Specify the on event for when this validation should be
|
414
|
+
# checked.
|
415
|
+
#
|
416
|
+
# An extra check is performed to avoid multiple validations.
|
417
|
+
#
|
418
|
+
# This example creates a validation for the 'save' event,
|
419
|
+
# and will add an error to the record if the 'name' property
|
420
|
+
# of the validating object is nil.
|
421
|
+
#
|
422
|
+
# === Example
|
423
|
+
#
|
424
|
+
# field = :name
|
425
|
+
#
|
426
|
+
# define_validation(:value, field, :save) do |object|
|
427
|
+
# object.errors.add(field, "You must set a value for #{field}") if object.send(field).nil?
|
428
|
+
# end
|
429
|
+
|
430
|
+
def define_validation(val, field, on = :save, &block)
|
431
|
+
vk = Validation::Key.new(val, field)
|
432
|
+
unless validations.find { |v| v[2] == vk }
|
433
|
+
validations! << [on, block, vk]
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
438
|
+
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
|
443
|
+
class Module # :nodoc: all
|
444
|
+
include Glue::Validation::ClassMethods
|
445
|
+
end
|
data/log/history.txt
CHANGED
File without changes
|
data/meta/manifest.txt
CHANGED
@@ -107,6 +107,7 @@ lib/core/facets/kernel/callstack.rb
|
|
107
107
|
lib/core/facets/kernel/constant.rb
|
108
108
|
lib/core/facets/kernel/deepcopy.rb
|
109
109
|
lib/core/facets/kernel/dir.rb
|
110
|
+
lib/core/facets/kernel/ergo.rb
|
110
111
|
lib/core/facets/kernel/instance.rb
|
111
112
|
lib/core/facets/kernel/metaid.rb
|
112
113
|
lib/core/facets/kernel/object.rb
|
@@ -139,7 +140,6 @@ lib/core/facets/module/require.rb
|
|
139
140
|
lib/core/facets/module/traits.rb
|
140
141
|
lib/core/facets/module.rb
|
141
142
|
lib/core/facets/nilclass
|
142
|
-
lib/core/facets/nilclass/ergo.rb
|
143
143
|
lib/core/facets/nilclass/status.rb
|
144
144
|
lib/core/facets/nilclass.rb
|
145
145
|
lib/core/facets/numeric
|
@@ -202,6 +202,7 @@ lib/methods/facets/array/contains.rb
|
|
202
202
|
lib/methods/facets/array/delete_unless.rb
|
203
203
|
lib/methods/facets/array/delete_values.rb
|
204
204
|
lib/methods/facets/array/delete_values_at.rb
|
205
|
+
lib/methods/facets/array/each_iteration.rb
|
205
206
|
lib/methods/facets/array/first.rb
|
206
207
|
lib/methods/facets/array/join_sentence.rb
|
207
208
|
lib/methods/facets/array/last.rb
|
@@ -521,6 +522,7 @@ lib/methods/facets/module/revisal.rb
|
|
521
522
|
lib/methods/facets/module/wrap.rb
|
522
523
|
lib/methods/facets/module/wrap_method.rb
|
523
524
|
lib/methods/facets/nilclass
|
525
|
+
lib/methods/facets/nilclass/ergo.rb
|
524
526
|
lib/methods/facets/nilclass/to_bool.rb
|
525
527
|
lib/methods/facets/nilclass/to_f.rb
|
526
528
|
lib/methods/facets/nilclass/to_h.rb
|
@@ -601,6 +603,7 @@ lib/methods/facets/string/succ.rb
|
|
601
603
|
lib/methods/facets/string/tab.rb
|
602
604
|
lib/methods/facets/string/taballto.rb
|
603
605
|
lib/methods/facets/string/tabto.rb
|
606
|
+
lib/methods/facets/string/titlecase.rb
|
604
607
|
lib/methods/facets/string/to_b.rb
|
605
608
|
lib/methods/facets/string/to_const.rb
|
606
609
|
lib/methods/facets/string/to_date.rb
|
@@ -729,11 +732,11 @@ lib/more/facets/syncarray.rb
|
|
729
732
|
lib/more/facets/synchash.rb
|
730
733
|
lib/more/facets/timer.rb
|
731
734
|
lib/more/facets/times.rb
|
732
|
-
lib/more/facets/tracepoint.rb
|
733
735
|
lib/more/facets/tuple.rb
|
734
736
|
lib/more/facets/typecast.rb
|
735
737
|
lib/more/facets/uninheritable.rb
|
736
738
|
lib/more/facets/uploadutils.rb
|
739
|
+
lib/more/facets/validation.rb
|
737
740
|
lib/more/facets/version.rb
|
738
741
|
lib/more/facets/yaml.rb
|
739
742
|
lib/more/facets/ziputils.rb
|
@@ -743,7 +746,7 @@ log/history.txt
|
|
743
746
|
log/release.txt
|
744
747
|
log/todo.txt
|
745
748
|
meta
|
746
|
-
meta/facets-2.1.
|
749
|
+
meta/facets-2.1.1.roll
|
747
750
|
meta/google_ad.html
|
748
751
|
meta/icli.yaml
|
749
752
|
meta/manifest.txt
|
@@ -768,6 +771,8 @@ task/release
|
|
768
771
|
task/special
|
769
772
|
task/special/quickopts
|
770
773
|
task/stats
|
774
|
+
task/svn
|
775
|
+
task/svn/tag
|
771
776
|
task/syntax
|
772
777
|
task/test
|
773
778
|
task/testeach
|
data/task/methods
CHANGED
data/task/svn/tag
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ratch
|
2
|
+
|
3
|
+
# Tag the current version.
|
4
|
+
|
5
|
+
main :svn_tag do
|
6
|
+
|
7
|
+
roll = glob('meta/*.roll')[0]
|
8
|
+
pkgn = File.basename(roll).chomp('.roll')
|
9
|
+
indx = pkgn.rindex('-')
|
10
|
+
name = pkgn[0...indx]
|
11
|
+
vers = pkgn[indx+1..-1]
|
12
|
+
|
13
|
+
developername = ENV['RUBYFORGE_USERNAME']
|
14
|
+
|
15
|
+
from = "svn+ssh://#{developername}@rubyforge.org/var/svn/facets/trunk"
|
16
|
+
twrd = "svn+ssh://#{developername}@rubyforge.org/var/svn/facets/tags/#{vers}"
|
17
|
+
mesg = "TAG #{vers}"
|
18
|
+
|
19
|
+
svn "copy #{from} #{twrd} -m '#{mesg}'"
|
20
|
+
|
21
|
+
end
|
22
|
+
|
metadata
CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.4.6
|
|
3
3
|
specification_version: 2
|
4
4
|
name: facets
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 2.1.
|
7
|
-
date: 2007-11-
|
8
|
-
summary:
|
6
|
+
version: 2.1.1
|
7
|
+
date: 2007-11-16 00:00:00 -05:00
|
8
|
+
summary: ""
|
9
9
|
require_paths:
|
10
10
|
- lib/methods
|
11
11
|
- lib/more
|
@@ -147,6 +147,7 @@ files:
|
|
147
147
|
- lib/core/facets/kernel/constant.rb
|
148
148
|
- lib/core/facets/kernel/deepcopy.rb
|
149
149
|
- lib/core/facets/kernel/dir.rb
|
150
|
+
- lib/core/facets/kernel/ergo.rb
|
150
151
|
- lib/core/facets/kernel/instance.rb
|
151
152
|
- lib/core/facets/kernel/metaid.rb
|
152
153
|
- lib/core/facets/kernel/object.rb
|
@@ -179,7 +180,6 @@ files:
|
|
179
180
|
- lib/core/facets/module/traits.rb
|
180
181
|
- lib/core/facets/module.rb
|
181
182
|
- lib/core/facets/nilclass
|
182
|
-
- lib/core/facets/nilclass/ergo.rb
|
183
183
|
- lib/core/facets/nilclass/status.rb
|
184
184
|
- lib/core/facets/nilclass.rb
|
185
185
|
- lib/core/facets/numeric
|
@@ -242,6 +242,7 @@ files:
|
|
242
242
|
- lib/methods/facets/array/delete_unless.rb
|
243
243
|
- lib/methods/facets/array/delete_values.rb
|
244
244
|
- lib/methods/facets/array/delete_values_at.rb
|
245
|
+
- lib/methods/facets/array/each_iteration.rb
|
245
246
|
- lib/methods/facets/array/first.rb
|
246
247
|
- lib/methods/facets/array/join_sentence.rb
|
247
248
|
- lib/methods/facets/array/last.rb
|
@@ -561,6 +562,7 @@ files:
|
|
561
562
|
- lib/methods/facets/module/wrap.rb
|
562
563
|
- lib/methods/facets/module/wrap_method.rb
|
563
564
|
- lib/methods/facets/nilclass
|
565
|
+
- lib/methods/facets/nilclass/ergo.rb
|
564
566
|
- lib/methods/facets/nilclass/to_bool.rb
|
565
567
|
- lib/methods/facets/nilclass/to_f.rb
|
566
568
|
- lib/methods/facets/nilclass/to_h.rb
|
@@ -641,6 +643,7 @@ files:
|
|
641
643
|
- lib/methods/facets/string/tab.rb
|
642
644
|
- lib/methods/facets/string/taballto.rb
|
643
645
|
- lib/methods/facets/string/tabto.rb
|
646
|
+
- lib/methods/facets/string/titlecase.rb
|
644
647
|
- lib/methods/facets/string/to_b.rb
|
645
648
|
- lib/methods/facets/string/to_const.rb
|
646
649
|
- lib/methods/facets/string/to_date.rb
|
@@ -769,11 +772,11 @@ files:
|
|
769
772
|
- lib/more/facets/synchash.rb
|
770
773
|
- lib/more/facets/timer.rb
|
771
774
|
- lib/more/facets/times.rb
|
772
|
-
- lib/more/facets/tracepoint.rb
|
773
775
|
- lib/more/facets/tuple.rb
|
774
776
|
- lib/more/facets/typecast.rb
|
775
777
|
- lib/more/facets/uninheritable.rb
|
776
778
|
- lib/more/facets/uploadutils.rb
|
779
|
+
- lib/more/facets/validation.rb
|
777
780
|
- lib/more/facets/version.rb
|
778
781
|
- lib/more/facets/yaml.rb
|
779
782
|
- lib/more/facets/ziputils.rb
|
@@ -783,7 +786,7 @@ files:
|
|
783
786
|
- log/release.txt
|
784
787
|
- log/todo.txt
|
785
788
|
- meta
|
786
|
-
- meta/facets-2.1.
|
789
|
+
- meta/facets-2.1.1.roll
|
787
790
|
- meta/google_ad.html
|
788
791
|
- meta/icli.yaml
|
789
792
|
- meta/manifest.txt
|
@@ -808,6 +811,8 @@ files:
|
|
808
811
|
- task/special
|
809
812
|
- task/special/quickopts
|
810
813
|
- task/stats
|
814
|
+
- task/svn
|
815
|
+
- task/svn/tag
|
811
816
|
- task/syntax
|
812
817
|
- task/test
|
813
818
|
- task/testeach
|
@@ -1,194 +0,0 @@
|
|
1
|
-
# TITLE:
|
2
|
-
#
|
3
|
-
# TracePoint
|
4
|
-
#
|
5
|
-
# SUMMARY:
|
6
|
-
#
|
7
|
-
# A TracePoint is a Binding with the addition event information.
|
8
|
-
# And it's a better way to use set_trace_func.
|
9
|
-
#
|
10
|
-
# COPYRIGHT:
|
11
|
-
#
|
12
|
-
# Copyright (c) 2005 Thomas Sawyer
|
13
|
-
#
|
14
|
-
# LICENSE:
|
15
|
-
#
|
16
|
-
# Ruby License
|
17
|
-
#
|
18
|
-
# This module is free software. You may use, modify, and/or redistribute this
|
19
|
-
# software under the same terms as Ruby.
|
20
|
-
#
|
21
|
-
# This program is distributed in the hope that it will be useful, but WITHOUT
|
22
|
-
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
23
|
-
# FOR A PARTICULAR PURPOSE.
|
24
|
-
#
|
25
|
-
# AUTHORS:
|
26
|
-
#
|
27
|
-
# - Thomas Sawyer
|
28
|
-
|
29
|
-
|
30
|
-
require 'facets/binding'
|
31
|
-
|
32
|
-
|
33
|
-
# = CodePoint
|
34
|
-
#
|
35
|
-
# This is the same as a Binding. Not really needed, but I like consistency :)
|
36
|
-
|
37
|
-
CodePoint = Binding
|
38
|
-
|
39
|
-
# = TracePoint
|
40
|
-
#
|
41
|
-
# A TracePoint is a Binding with the addition of event information.
|
42
|
-
# Among other things, it functions very well as the join-point for
|
43
|
-
# Event-based AOP.
|
44
|
-
#
|
45
|
-
# == Usage
|
46
|
-
#
|
47
|
-
# TracePoint.trace { |tp|
|
48
|
-
# puts "#{tp.self.class}\t#{tp.called}\t#{tp.event}\t#{tp.return?}\t#{tp.back == tp.bind}"
|
49
|
-
# }
|
50
|
-
#
|
51
|
-
# 1 + 1
|
52
|
-
#
|
53
|
-
# produces
|
54
|
-
#
|
55
|
-
# Class trace return true false
|
56
|
-
# Object line false false
|
57
|
-
# Fixnum + c-call false false
|
58
|
-
# Fixnum + c-return false false
|
59
|
-
#
|
60
|
-
# == Notes
|
61
|
-
#
|
62
|
-
# You can't subclass Binding, so we delegate (which is better anyway).
|
63
|
-
|
64
|
-
class TracePoint #< Codepoint
|
65
|
-
|
66
|
-
# -- class ---------------------
|
67
|
-
class << self
|
68
|
-
|
69
|
-
@@active = false
|
70
|
-
|
71
|
-
def active ; @@active ; end
|
72
|
-
|
73
|
-
def active=(x)
|
74
|
-
@@active = x ? true : false
|
75
|
-
unless @@active
|
76
|
-
set_trace_func nil
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
# Trace execution using a TracePoint.
|
81
|
-
def trace # :yield:
|
82
|
-
if active
|
83
|
-
bb_stack = []
|
84
|
-
set_trace_func proc{ |e, f, l, m, b, k|
|
85
|
-
#(p e, f, l, m, b, k, @@bb_stack; puts "---") if $DEBUG
|
86
|
-
if ['call','c-call','class'].include?(e)
|
87
|
-
bb_stack << b
|
88
|
-
elsif ['return','c-return','end'].include?(e)
|
89
|
-
bb = bb_stack.pop
|
90
|
-
end
|
91
|
-
b = bb if ! b # this sucks!
|
92
|
-
tp = TracePoint.new(e,m,b,bb)
|
93
|
-
yield(tp)
|
94
|
-
}
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
end #class
|
99
|
-
|
100
|
-
# -- instance -------------------
|
101
|
-
|
102
|
-
attr_accessor :event, :binding, :back_binding
|
103
|
-
|
104
|
-
# Until Ruby has a built-in way to get the name of the calling method
|
105
|
-
# that information must be passed into the TracePoint.
|
106
|
-
def initialize( event, method, bind, back_binding=bind )
|
107
|
-
@event = event
|
108
|
-
@method = method
|
109
|
-
@binding = bind
|
110
|
-
@back_binding = back_binding
|
111
|
-
end
|
112
|
-
|
113
|
-
# shorthand for binding
|
114
|
-
def bind ; @binding ; end
|
115
|
-
|
116
|
-
# shorthand for back_binding
|
117
|
-
def back ; @back_binding ; end
|
118
|
-
|
119
|
-
# Delegates "self" to the binding which
|
120
|
-
# in turn delegates the binding object.
|
121
|
-
def self ; @binding.self ; end
|
122
|
-
|
123
|
-
# Returns the name of the event's method.
|
124
|
-
# This could delegate to the binding if Ruby had
|
125
|
-
# an internal way to retrieve the current method name.
|
126
|
-
def callee ; @method ; end
|
127
|
-
#def method ; @method ; end # TODO Conflict with Kernel#method?
|
128
|
-
alias_method( :called, :callee ) # TODO deprecate
|
129
|
-
alias_method( :method_name, :called ) # TODO deprecate
|
130
|
-
|
131
|
-
# delegate to binding
|
132
|
-
#def method_missing(meth, *args, &blk)
|
133
|
-
# @binding.send(meth, *args, &blk)
|
134
|
-
#end
|
135
|
-
|
136
|
-
### methods for working with events
|
137
|
-
|
138
|
-
EVENT_MAP = {
|
139
|
-
:all => ['call', 'c-call', 'return', 'c-return', 'line', 'class', 'end', 'raise'],
|
140
|
-
:before => ['call', 'c-call'],
|
141
|
-
:after => ['return', 'c-return'],
|
142
|
-
:call => ['call'],
|
143
|
-
:return => ['return'],
|
144
|
-
:ccall => ['c-call'],
|
145
|
-
:creturn => ['c-return'],
|
146
|
-
:line => ['line'],
|
147
|
-
:class => ['class'],
|
148
|
-
:end => ['end'],
|
149
|
-
:raise => ['raise']
|
150
|
-
}
|
151
|
-
def event_map(e) ; EVENT_MAP[e] ; end
|
152
|
-
|
153
|
-
# Is the trace point defined or undefined?
|
154
|
-
def event? ; !! @event ; end
|
155
|
-
def eventless? ; ! @event ; end
|
156
|
-
|
157
|
-
# For use in case conditions
|
158
|
-
def ===(e)
|
159
|
-
EVENT_MAP[e].include?(@event)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Creates an <event>? method for each of the above event mappings.
|
163
|
-
EVENT_MAP.each_pair { |m,v|
|
164
|
-
define_method( "#{m}?" ){ v.include?(@event) }
|
165
|
-
}
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
# _____ _
|
172
|
-
# |_ _|__ ___| |_
|
173
|
-
# | |/ _ \/ __| __|
|
174
|
-
# | | __/\__ \ |_
|
175
|
-
# |_|\___||___/\__|
|
176
|
-
#
|
177
|
-
|
178
|
-
# TODO
|
179
|
-
|
180
|
-
=begin #test
|
181
|
-
|
182
|
-
# Note: TracePoint will probably prove tricky to test, since
|
183
|
-
# manipulating set_trace_func tends to wreak havoc on errors,
|
184
|
-
# the call stack, and so on.
|
185
|
-
|
186
|
-
TracePoint.active = true
|
187
|
-
|
188
|
-
TracePoint.trace { |tp|
|
189
|
-
puts "#{tp.self.class}\t#{tp.called}\t#{tp.event}\t#{tp.return?}\t#{tp.back == tp.bind}"
|
190
|
-
}
|
191
|
-
|
192
|
-
1 + 1
|
193
|
-
|
194
|
-
=end
|