glue 0.20.0 → 0.21.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/CHANGELOG +161 -110
- data/INSTALL +12 -12
- data/README +1 -1
- data/Rakefile +43 -45
- data/doc/AUTHORS +5 -5
- data/doc/LICENSE +3 -3
- data/doc/RELEASES +32 -24
- data/install.rb +7 -17
- data/lib/facet/object/alias_class.rb +12 -0
- data/lib/glue.rb +35 -35
- data/lib/glue/array.rb +46 -46
- data/lib/glue/aspects.rb +199 -209
- data/lib/glue/attribute.rb +15 -15
- data/lib/glue/autoreload.rb +1 -1
- data/lib/glue/builder.rb +48 -0
- data/lib/glue/builder/xml.rb +114 -0
- data/lib/glue/cache.rb +189 -0
- data/lib/glue/configuration.rb +108 -90
- data/lib/glue/flexob.rb +17 -17
- data/lib/glue/hash.rb +71 -71
- data/lib/glue/helper.rb +12 -12
- data/lib/glue/idgen.rb +9 -0
- data/lib/glue/idgen/md5.rb +24 -0
- data/lib/glue/idgen/sequential.rb +15 -0
- data/lib/glue/literal_method.rb +44 -0
- data/lib/glue/localization.rb +130 -0
- data/lib/glue/logger.rb +98 -98
- data/lib/glue/misc.rb +7 -7
- data/lib/glue/mixins.rb +19 -19
- data/lib/glue/number.rb +8 -8
- data/lib/glue/object.rb +2 -2
- data/lib/glue/pool.rb +43 -43
- data/lib/glue/property.rb +392 -392
- data/lib/glue/sanitize.rb +34 -34
- data/lib/glue/settings.rb +1 -1
- data/lib/glue/snapshot.rb +104 -0
- data/lib/glue/string.rb +129 -129
- data/lib/glue/time.rb +53 -53
- data/lib/glue/uri.rb +162 -162
- data/lib/glue/validation.rb +421 -421
- data/lib/vendor/blankslate.rb +53 -0
- data/test/glue/builder/tc_xml.rb +56 -0
- data/test/glue/tc_aspects.rb +90 -90
- data/test/glue/tc_attribute.rb +11 -11
- data/test/glue/tc_builder.rb +30 -0
- data/test/glue/tc_configuration.rb +97 -97
- data/test/glue/tc_flexob.rb +10 -10
- data/test/glue/tc_hash.rb +23 -23
- data/test/glue/tc_localization.rb +49 -0
- data/test/glue/tc_logger.rb +31 -31
- data/test/glue/tc_numbers.rb +9 -9
- data/test/glue/tc_property.rb +67 -67
- data/test/glue/tc_property_mixins.rb +17 -17
- data/test/glue/tc_property_type_checking.rb +13 -13
- data/test/glue/tc_strings.rb +94 -94
- data/test/glue/tc_uri.rb +65 -65
- data/test/glue/tc_validation.rb +196 -196
- metadata +26 -4
data/lib/glue/aspects.rb
CHANGED
@@ -5,20 +5,20 @@ module Glue
|
|
5
5
|
# An Aspect is a class that defines advices.
|
6
6
|
|
7
7
|
class Aspect
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
class << self
|
9
|
+
def wrap(target, methods = target.instance_methods, pre = :pre, post = :post)
|
10
|
+
target.send(:include, Aspects) unless target.ancestors.include?(Aspects)
|
11
|
+
target.wrap(self, :pre => pre, :post => post)
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method :observe, :wrap
|
15
|
+
end
|
16
|
+
|
17
|
+
def wrap(target, methods = target.instance_methods, pre = :pre, post = :post)
|
18
|
+
target.send(:include, Aspects) unless target.ancestors.include?(Aspects)
|
19
|
+
target.wrap(self, :pre => pre, :post => post)
|
20
|
+
end
|
21
|
+
alias_method :observe, :wrap
|
22
22
|
end
|
23
23
|
|
24
24
|
# Add support for Aspect Oriented Programming (AOP).
|
@@ -26,208 +26,198 @@ end
|
|
26
26
|
# === Examples
|
27
27
|
#
|
28
28
|
# class Controller
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
29
|
+
# pre :force_login, :where => :prepend
|
30
|
+
# wrap Benchmark, :on => :index
|
31
|
+
# post :taraa, :on => login
|
32
32
|
# end
|
33
33
|
#
|
34
34
|
# module Timestamped
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
35
|
+
# pre :on => :og_insert { |this| this.create_time = Time.now }
|
36
|
+
# pre :on => :og_update { |this| this.update_time = Time.now }
|
37
|
+
# pre :on => [:og_insert, :og_update] { |this| this.create_time = Time.now }
|
38
38
|
# end
|
39
39
|
|
40
40
|
module Aspects
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
options.update(args.pop) if args.last.is_a?(Hash)
|
222
|
-
|
223
|
-
for aspect in args
|
224
|
-
self.advices << Advice.new(aspect, options)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
alias_method :around, :wrap
|
228
|
-
alias_method :observer, :wrap
|
229
|
-
|
230
|
-
end
|
42
|
+
# Store the code and the metadata (options) for
|
43
|
+
# an Advice.
|
44
|
+
|
45
|
+
Advice = Struct.new(:code, :options)
|
46
|
+
|
47
|
+
# Apply the advices to the target class.
|
48
|
+
|
49
|
+
def self.wrap(target, methods = target.instance_methods)
|
50
|
+
include_advice_modules(target)
|
51
|
+
|
52
|
+
for m in [methods].flatten
|
53
|
+
args = []
|
54
|
+
target.instance_method(m).arity.times { |i| args << "a#{i}" }
|
55
|
+
args = args.join(',')
|
56
|
+
|
57
|
+
target.module_eval <<-end_eval, __FILE__, __LINE__
|
58
|
+
alias_method :__unwrapped_#{m}, :#{m}
|
59
|
+
def #{m}(#{args})
|
60
|
+
#{gen_advice_code(m, target.advices, :pre)}
|
61
|
+
__unwrapped_#{m}(#{args})
|
62
|
+
#{gen_advice_code(m, target.advices, :post)}
|
63
|
+
end
|
64
|
+
end_eval
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Include Modules that define advices.
|
69
|
+
|
70
|
+
def self.include_advice_modules(target)
|
71
|
+
for a in target.advices
|
72
|
+
if a.code.is_a?(Module) and (!a.code.class.ancestors.include?(Class))
|
73
|
+
target.module_eval %{ include #{a.code} }
|
74
|
+
|
75
|
+
options = a.options.reject { |k,v| k == :pre || k == :post }
|
76
|
+
|
77
|
+
method = (a.options[:pre] || 'pre').to_s
|
78
|
+
if a.code.instance_methods.include?(method)
|
79
|
+
options.update(:where => :prepend, :join => :pre)
|
80
|
+
target.advices << Advice.new(method, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
method = (a.options[:post] || 'post').to_s
|
84
|
+
if a.code.instance_methods.include?(method)
|
85
|
+
options.update(:where => :append, :join => :post)
|
86
|
+
target.advices << Advice.new(method, options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Remove the original advice.
|
92
|
+
|
93
|
+
target.advices.delete_if do |a|
|
94
|
+
a.code.is_a?(Module) and (!a.code.class.ancestors.include?(Class))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generates the code to call the aspects.
|
99
|
+
|
100
|
+
def self.gen_advice_code(method, advices, join = :pre) # :nodoc:
|
101
|
+
code = ''
|
102
|
+
|
103
|
+
advices.each_with_index do |advice, idx|
|
104
|
+
o = options = advice.options
|
105
|
+
|
106
|
+
if only = options[:only] || options[:on]
|
107
|
+
next unless [only].flatten.include?(method.to_sym)
|
108
|
+
elsif except = options[:except]
|
109
|
+
next if [except].flatten.include?(method.to_sym)
|
110
|
+
end
|
111
|
+
|
112
|
+
advice = advice.code
|
113
|
+
|
114
|
+
if advice.is_a?(Symbol) or advice.is_a?(String)
|
115
|
+
next if o[:join] != join
|
116
|
+
code << "#{advice}; "
|
117
|
+
elsif advice.respond_to?('call')
|
118
|
+
next if o[:join] != join
|
119
|
+
code << "self.class.advices[#{idx}].code.call(self); "
|
120
|
+
elsif advice.is_a?(Class)
|
121
|
+
if advice.class.ancestors.include?(Class)
|
122
|
+
if m = o[join] and advice.methods.include?(m.to_s)
|
123
|
+
code << "#{advice}.#{m}(self); "
|
124
|
+
end
|
125
|
+
else
|
126
|
+
# Module, allready handled.
|
127
|
+
end
|
128
|
+
else
|
129
|
+
if m = o[join] and advice.methods.include?(m.to_s)
|
130
|
+
code << "self.class.advices[#{idx}].code.#{m}(self); "
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
return code
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.append_features(base)
|
139
|
+
super
|
140
|
+
base.extend(ClassMethods)
|
141
|
+
|
142
|
+
base.module_eval %{
|
143
|
+
Glue::PropertyUtils.enchant(self)
|
144
|
+
|
145
|
+
def self.advices
|
146
|
+
__meta[:advices] || []
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.advices=(advices)
|
150
|
+
__meta[:advices] = advices
|
151
|
+
end
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
module ClassMethods
|
156
|
+
|
157
|
+
# Add a pre (before) advice.
|
158
|
+
|
159
|
+
def pre(*args, &block)
|
160
|
+
o = options = {
|
161
|
+
:join => :pre,
|
162
|
+
:where => :prepend,
|
163
|
+
}
|
164
|
+
options.update(args.pop) if args.last.is_a?(Hash)
|
165
|
+
|
166
|
+
if block_given?
|
167
|
+
advices = [ Advice.new(block, options) ]
|
168
|
+
else
|
169
|
+
advices = args.collect { |a| Advice.new(a, options) }
|
170
|
+
end
|
171
|
+
|
172
|
+
if options[:where] == :prepend
|
173
|
+
self.advices = advices + self.advices
|
174
|
+
else
|
175
|
+
self.advices = self.advices + advices
|
176
|
+
end
|
177
|
+
end
|
178
|
+
alias_method :before, :pre
|
179
|
+
|
180
|
+
# Add a post (after) advice.
|
181
|
+
|
182
|
+
def post(*args, &block)
|
183
|
+
o = options = {
|
184
|
+
:join => :post,
|
185
|
+
:where => :append,
|
186
|
+
}
|
187
|
+
options.update(args.pop) if args.last.is_a?(Hash)
|
188
|
+
|
189
|
+
if block_given?
|
190
|
+
advices = [ Advice.new(block, options) ]
|
191
|
+
else
|
192
|
+
advices = args.collect { |a| Advice.new(a, options) }
|
193
|
+
end
|
194
|
+
|
195
|
+
if options[:where] == :prepend
|
196
|
+
self.advices = advices + self.advices
|
197
|
+
else
|
198
|
+
self.advices = self.advices + advices
|
199
|
+
end
|
200
|
+
end
|
201
|
+
alias_method :after, :post
|
202
|
+
|
203
|
+
# Add a wrap (arround) aspect. An aspect is a class that
|
204
|
+
# responds to the before and after advices.
|
205
|
+
|
206
|
+
def wrap(*args)
|
207
|
+
o = options = {
|
208
|
+
:pre => :pre,
|
209
|
+
:post => :post
|
210
|
+
}
|
211
|
+
options.update(args.pop) if args.last.is_a?(Hash)
|
212
|
+
|
213
|
+
for aspect in args
|
214
|
+
self.advices << Advice.new(aspect, options)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
alias_method :around, :wrap
|
218
|
+
alias_method :observer, :wrap
|
219
|
+
|
220
|
+
end
|
231
221
|
|
232
222
|
end
|
233
223
|
|
data/lib/glue/attribute.rb
CHANGED
@@ -17,15 +17,15 @@
|
|
17
17
|
|
18
18
|
class Module # :nodoc:
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
# A macro that creates a reader method for class/module
|
21
|
+
# attributes.
|
22
|
+
|
23
23
|
def mattr_reader(*params)
|
24
|
-
|
25
|
-
|
24
|
+
default = if params.last.is_a?(Symbol) then nil else params.pop end
|
25
|
+
|
26
26
|
for sym in params
|
27
|
-
|
28
|
-
|
27
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
28
|
+
|
29
29
|
if not defined?(@@#{sym.id2name})
|
30
30
|
@@#{sym.id2name} = #{default.inspect}
|
31
31
|
end
|
@@ -39,14 +39,14 @@ class Module # :nodoc:
|
|
39
39
|
end
|
40
40
|
alias_method :cattr_reader, :mattr_reader
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# A macro that creates a writer method for class/module
|
43
|
+
# attributes.
|
44
44
|
|
45
45
|
def mattr_writer(*params)
|
46
|
-
|
47
|
-
|
46
|
+
default = if params.last.is_a?(Symbol) then nil else params.pop end
|
47
|
+
|
48
48
|
for sym in params
|
49
|
-
|
49
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
50
50
|
|
51
51
|
if not defined?(@@#{sym.id2name})
|
52
52
|
@@#{sym.id2name} = #{default.inspect.inspect}
|
@@ -65,15 +65,15 @@ class Module # :nodoc:
|
|
65
65
|
end
|
66
66
|
alias_method :cattr_writer, :cattr_writer
|
67
67
|
|
68
|
-
|
69
|
-
|
68
|
+
# A macro that creates a reader and a writer method for
|
69
|
+
# class/module attributes.
|
70
70
|
|
71
71
|
def mattr_accessor(*syms)
|
72
72
|
mattr_reader(*syms)
|
73
73
|
mattr_writer(*syms)
|
74
74
|
end
|
75
75
|
alias_method :cattr_accessor, :mattr_accessor
|
76
|
-
|
76
|
+
|
77
77
|
end
|
78
78
|
|
79
79
|
# * George Moschovitis <gm@navel.gr>
|