glue 0.0.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: logger.rb 282 2005-03-10 12:24:53Z gmosx $
4
+
5
+ require 'logger'
6
+
7
+ # A simple wrapper arround the Ruby logger. Mainly for
8
+ # compatibility purposes.
9
+
10
+ class Logger
11
+ alias_method :devel, :debug
12
+ alias_method :fine, :debug
13
+
14
+ # Prints a trace message to DEBUGLOG (at debug level).
15
+ # Useful for emitting the value of variables, etc. Use
16
+ # like this:
17
+ #
18
+ # x = y = 5
19
+ # trace 'x' # -> 'x = 5'
20
+ # trace 'x ** y' # -> 'x ** y = 3125'
21
+ #
22
+ # If you have a more complicated value, like an array of
23
+ # hashes, then you'll probably want to use an alternative
24
+ # output format. For instance:
25
+ #
26
+ # trace 'value', :yaml
27
+ #
28
+ # Valid output format values (the _style_ parameter) are:
29
+ #
30
+ # :p :inspect
31
+ # :pp (pretty-print, using 'pp' library)
32
+ # :s :to_s
33
+ # :y :yaml :to_yaml (using the 'yaml' library')
34
+ #
35
+ # The default is <tt>:p</tt>.
36
+ #
37
+ # CREDITS:
38
+ #
39
+ # This code comes straight from the dev-utils Gem.
40
+ # Author: Gavin Sinclair <gsinclair@soyabean.com.au>
41
+
42
+ def trace(expr, style=:p)
43
+ unless expr.respond_to? :to_str
44
+ warn "trace: Can't evaluate the given value: #{caller.first}"
45
+ else
46
+ require 'extensions/binding'
47
+
48
+ Binding.of_caller do |b|
49
+ value = b.eval(expr.to_str)
50
+ formatter = TRACE_STYLES[style] || :inspect
51
+ case formatter
52
+ when :pp then require 'pp'
53
+ when :y, :yaml, :to_yaml then require 'yaml'
54
+ end
55
+ value_s = value.send(formatter)
56
+ message = "#{expr} = #{value_s}"
57
+ lines = message.split(/\n/)
58
+ indent = " "
59
+ debug(lines.shift)
60
+ lines.each do |line|
61
+ debug(indent + line)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ TRACE_STYLES = {} # :nodoc:
67
+ TRACE_STYLES.update( :pp => :pp_s, :s => :to_s, :p => :inspect, :y => :to_yaml,
68
+ :yaml => :to_yaml, :inspect => :inspect, :to_yaml => :to_yaml )
69
+
70
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
71
+ # Global logger interface.
72
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73
+
74
+ # Override the logger with your custom version.
75
+ #
76
+ @@global_logger = Logger.new(STDERR)
77
+
78
+ def self.set(logger)
79
+ @@global_logger = logger
80
+ end
81
+
82
+ def self.get
83
+ @@global_logger
84
+ end
85
+
86
+ def self.warn(str)
87
+ @@global_logger.warn(str)
88
+ end
89
+
90
+ def self.info(str)
91
+ @@global_logger.info(str)
92
+ end
93
+
94
+ def self.debug(str)
95
+ @@global_logger.debug(str)
96
+ end
97
+
98
+ def self.error(str)
99
+ @@global_logger.error(str)
100
+ end
101
+
102
+ #--
103
+ # Saddly have to duplicate the code to make
104
+ # Binding.of_caller work.
105
+ #++
106
+ def self.trace(expr, style=:p)
107
+ unless expr.respond_to? :to_str
108
+ warn "trace: Can't evaluate the given value: #{caller.first}"
109
+ else
110
+ require 'extensions/binding'
111
+
112
+ Binding.of_caller do |b|
113
+ value = b.eval(expr.to_str)
114
+ formatter = TRACE_STYLES[style] || :inspect
115
+ case formatter
116
+ when :pp then require 'pp'
117
+ when :y, :yaml, :to_yaml then require 'yaml'
118
+ end
119
+ value_s = value.send(formatter)
120
+ message = "#{expr} = #{value_s}"
121
+ lines = message.split(/\n/)
122
+ indent = " "
123
+ debug(lines.shift)
124
+ lines.each do |line|
125
+ debug(indent + line)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
132
+
133
+ private
134
+
135
+ # the default Ruby logger has a hardwired silly format.
136
+ # we use some Ruby magic to fix it!
137
+
138
+ remove_const "Format"
139
+
140
+ Format = "%5s: %s\n"
141
+
142
+ def format_message(severity, timestamp, msg, progname)
143
+ Format % [severity, msg]
144
+ end
145
+
146
+ end
147
+
data/lib/glue/misc.rb ADDED
@@ -0,0 +1,14 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id$
4
+
5
+ # Executes a Ruby block without warnings.
6
+
7
+ def silence_warnings
8
+ old_verbose, $VERBOSE = $VERBOSE, nil
9
+ begin
10
+ yield
11
+ ensure
12
+ $VERBOSE = old_verbose
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id$
4
+
5
+ module N
6
+
7
+ # Generic expiring functionality mixin.
8
+
9
+ module Expirable
10
+ attr_accessor :expires
11
+
12
+ # Set the expires timeout for this entry.
13
+
14
+ def expires_after(timeout = (60*60*24))
15
+ @expires = Time.now + timeout
16
+ end
17
+
18
+ # Set the expire timeout for this entry. The timeout happens
19
+ # after (base + rand(spread)) seconds.
20
+
21
+ def expires_spread(base, spread)
22
+ @expires = Time.now + base + rand(spread)
23
+ end
24
+
25
+ # Is this entry expired?
26
+
27
+ def expired?
28
+ if @expires.nil? or (Time.now > @expires)
29
+ return true
30
+ else
31
+ return false
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,24 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: number.rb 282 2005-03-10 12:24:53Z gmosx $
4
+
5
+ module N;
6
+
7
+ # Implement as a module to avoid class polution. You can
8
+ # still use Ruby's advanced features to include the module in your
9
+ # class. Passing the object to act upon allows to check for nil,
10
+ # which isn't possible if you use self.
11
+
12
+ module NumberUtils
13
+
14
+ # Returns the multiple ceil of a number
15
+ #
16
+ def self.ceil_multiple(num, multiple)
17
+ # gmosx: to_f is needed!s
18
+ # gmosx: IS THERE a more optimized way to do this?
19
+ return ((num.to_f/multiple).ceil*multiple)
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,32 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: object.rb 282 2005-03-10 12:24:53Z gmosx $
4
+
5
+ # Code from RubyOnRails (http://www.rubyonrails.com)
6
+
7
+ class Object #:nodoc:
8
+ def remove_subclasses_of(superclass)
9
+ subclasses_of(superclass).each { |subclass| Object.send(:remove_const, subclass) rescue nil }
10
+ end
11
+
12
+ def subclasses_of(superclass)
13
+ subclasses = []
14
+ ObjectSpace.each_object(Class) do |k|
15
+ next if !k.ancestors.include?(superclass) || superclass == k || k.to_s.include?("::") || subclasses.include?(k.to_s)
16
+ subclasses << k.to_s
17
+ end
18
+ subclasses
19
+ end
20
+ end
21
+
22
+ # Code from RubyOnRails (http://www.rubyonrails.com)
23
+
24
+ class Class #:nodoc:
25
+ def remove_subclasses
26
+ Object.remove_subclasses_of(self)
27
+ end
28
+
29
+ def subclasses
30
+ Object.subclasses_of(self)
31
+ end
32
+ end
data/lib/glue/pool.rb ADDED
@@ -0,0 +1,60 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: pool.rb 282 2005-03-10 12:24:53Z gmosx $
4
+
5
+ require 'thread'
6
+ require 'monitor'
7
+
8
+ module N
9
+
10
+ # Generalized object pool implementation. Implemented as a thread
11
+ # safe stack. Exclusive locking is needed both for push and pop.
12
+ #
13
+ # INVESTIGATE: Could use the SizedQueue/Queue.
14
+
15
+ class Pool < Array
16
+ include MonitorMixin
17
+
18
+ def initialize
19
+ super
20
+ @cv = new_cond()
21
+ end
22
+
23
+ # Add, restore an object to the pool.
24
+
25
+ def push(obj)
26
+ synchronize do
27
+ super
28
+ @cv.signal()
29
+ end
30
+ end
31
+
32
+ # Obtain an object from the pool.
33
+
34
+ def pop
35
+ synchronize do
36
+ @cv.wait_while { empty? }
37
+ super
38
+ end
39
+ end
40
+
41
+ # Obtains an object, passes it to a block for processing
42
+ # and restores it to the pool.
43
+
44
+ def obtain
45
+ result = nil
46
+
47
+ begin
48
+ obj = pop()
49
+
50
+ result = yield(obj)
51
+ ensure
52
+ push(obj)
53
+ end
54
+
55
+ return result
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,408 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # * Michael Neumann <mneumann@ntecs.de>
3
+ # (c) 2004-2005 Navel, all rights reserved.
4
+ # $Id: property.rb 282 2005-03-10 12:24:53Z gmosx $
5
+
6
+ require 'glue/attribute'
7
+ require 'glue/array'
8
+ require 'glue/hash'
9
+
10
+ module N
11
+
12
+ # Ruby attributes are typeless and generally this is good.
13
+ # Some times we need extra metadata though, for example in
14
+ # relational mapping, or web form population.
15
+ #
16
+ # Only Fixnums, Strings, Floats, Times, Booleans are
17
+ # converted.
18
+ #
19
+ # The default = methods do not force the types. A special
20
+ # __force_set method should be used instead.
21
+ #--
22
+ # TODO:
23
+ # Perhaps a sync is needed in evals (!!!!)
24
+ #++
25
+
26
+ class Property
27
+
28
+ # If set to true, perform type checking on property set.
29
+ # Useful when debugging.
30
+
31
+ cattr_accessor :type_checking, false
32
+
33
+ # The symbol of the property.
34
+
35
+ attr_accessor :symbol
36
+
37
+ # The string representation of the symbol.
38
+
39
+ attr_accessor :name
40
+
41
+ # The class of the property.
42
+
43
+ attr_accessor :klass
44
+
45
+ # Additional metadata (like sql declaration, sql index, etc)
46
+ # Here is a list of predefined metadata:
47
+ #
48
+ # [+:reader+]
49
+ # create reader?
50
+ #
51
+ # [+:writer+]
52
+ # create writer?
53
+ #
54
+ # [+:sql_index+]
55
+ # create an sql index for the column this poperty maps to?
56
+ #
57
+ # You can use this mechanism to add your own, custom,
58
+ # metadata.
59
+
60
+ attr_accessor :meta
61
+
62
+ def initialize(symbol, klass, meta = {})
63
+ @symbol, @klass = symbol, klass
64
+ @meta = meta
65
+ @name = @symbol.to_s()
66
+ end
67
+
68
+ def ==(other)
69
+ return @symbol == other.symbol
70
+ end
71
+
72
+ def to_s
73
+ return name
74
+ end
75
+
76
+ end
77
+
78
+ # A collection of Property related utility methods.
79
+
80
+ module PropertyUtils
81
+
82
+ # Add accessors to the properties to the given target
83
+ # (Module or Class). For simplicity also create the
84
+ # meta accessors.
85
+ #
86
+ # [+target+]
87
+ # The target class or module
88
+ #--
89
+ # gmosx: Perhaps we 'll optimize this in the future.
90
+ #++
91
+
92
+ def self.enchant(target, force = false)
93
+ unless target.singleton_methods.include?('__props')
94
+ target.module_eval %{
95
+ @@__meta = N::SafeHash.new
96
+ @@__props = N::SafeArray.new
97
+
98
+ def self.__props
99
+ @@__props
100
+ end
101
+
102
+ def self.__props=(props)
103
+ @@__props = props
104
+ end
105
+
106
+ def self.__meta
107
+ @@__meta
108
+ end
109
+
110
+ def self.__meta=(meta)
111
+ @@__meta = meta
112
+ end
113
+ }
114
+ end
115
+ end
116
+
117
+ # Copy properties from src (Module or Class) to dest.
118
+
119
+ def self.copy_props(src, dest)
120
+ src.__props.each do |p|
121
+ add_prop(dest, p)
122
+ end
123
+
124
+ # copy the metadata.
125
+ src.__meta.each do |k, val|
126
+ dest.__meta[k] = val.dup
127
+ # val.each { |v| dest.meta(k, v) } if val
128
+ end
129
+ end
130
+
131
+ # Add the property to the target (Class or Module)
132
+
133
+ def self.add_prop(target, prop)
134
+ if idx = target.__props.index(prop)
135
+ # override in case of duplicates. Keep the order of the props.
136
+ target.__props[idx] = prop
137
+ else
138
+ target.__props << prop
139
+ end
140
+
141
+ # Store the property in the :props_and_relations
142
+ # metadata array.
143
+
144
+ target.meta :props_and_relations, prop
145
+
146
+ # Precompile the property read/write methods
147
+
148
+ s, klass = prop.symbol, prop.klass
149
+
150
+ if prop.meta[:reader]
151
+ target.module_eval %{
152
+ def #{s}
153
+ return @#{s}
154
+ end
155
+ }
156
+ end
157
+
158
+ # gmosx: __force_xxx reuses xxx= to allow for easier
159
+ # overrides.
160
+
161
+ if prop.meta[:writer]
162
+ target.module_eval %{
163
+ #{prop_setter(prop)}
164
+
165
+ def __force_#{s}(val)
166
+ self.#{s}=(} + case klass.name
167
+ when Fixnum.name
168
+ "val.to_i()"
169
+ when String.name
170
+ "val.to_s()"
171
+ when Float.name
172
+ "val.to_f()"
173
+ when Time.name
174
+ "Time.parse(val.to_s())"
175
+ when TrueClass.name, FalseClass.name
176
+ "val.to_i() > 0"
177
+ else
178
+ "val"
179
+ end + %{)
180
+ end
181
+ }
182
+ end
183
+ end
184
+
185
+ # Generates the property setter code. Can be overriden
186
+ # to support extra functionality (example: markup)
187
+
188
+ def self.prop_setter(prop)
189
+ s = prop.symbol
190
+
191
+ code = %{
192
+ def #{s}=(val)
193
+ }
194
+
195
+ if N::Property.type_checking
196
+ code << %{
197
+ unless #{prop.klass} == val.class
198
+ raise "Invalid type, expected '#{prop.klass}', is '\#\{val.class\}'."
199
+ end
200
+ }
201
+ end
202
+
203
+ code << %{
204
+ @#{s} = val
205
+ end
206
+ }
207
+
208
+ return code
209
+ end
210
+
211
+ # Get the property metadata for the given symbol.
212
+
213
+ def self.get_prop(klass, sym)
214
+ return klass.__props.find { |p| p.symbol == sym }
215
+ end
216
+
217
+ # Include meta-language mixins
218
+
219
+ def self.include_meta_mixins(target)
220
+ target.module_eval %{ include N::Validation } if defined?(N::Validation)
221
+ # gmosx: TODO, make Og::MetaLanguage equivalent to Validation.
222
+ target.module_eval %{ extend Og::MetaLanguage } if defined?(Og::MetaLanguage)
223
+ end
224
+
225
+ # Resolves the parameters passed to the propxxx macros
226
+ # to generate the meta, klass and symbols variables. This
227
+ # way the common functionality is factored out.
228
+ #
229
+ # [+params+]
230
+ # The params to resolve.
231
+ # [+one_symbol+]
232
+ # If true, only resolves one symbol (used in prop).
233
+
234
+ def self.resolve_prop_params(*params)
235
+ meta = {}
236
+ klass = Object
237
+ symbols = []
238
+
239
+ for param in params.flatten
240
+ if param.is_a?(Class)
241
+ klass = param
242
+ elsif param.is_a?(Symbol)
243
+ symbols << param
244
+ elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
245
+ writer = param
246
+ elsif param.is_a?(Hash)
247
+ # the meta hash.
248
+ meta.update(param) { |k, a, b| [a,b].join(' ') }
249
+ else
250
+ raise 'Error when defining property!'
251
+ end
252
+ end
253
+
254
+ raise 'No symbols provided!' if symbols.empty?
255
+
256
+ return meta, klass, symbols
257
+ end
258
+
259
+ end
260
+
261
+ end # module
262
+
263
+ class Module
264
+
265
+ # Define a property (== typed attribute)
266
+ # This works like Ruby's standard attr method, ie creates
267
+ # only one property.
268
+ #
269
+ # Use the prop_reader, prop_writer, prop_accessor methods
270
+ # for multiple properties.
271
+ #
272
+ # Examples:
273
+ # prop String, :name, :sql => "char(32), :sql_index => "name(32)"
274
+ # --> creates only writer.
275
+ # prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
276
+ # --> creates reader and writer.
277
+
278
+ def prop(*params)
279
+ meta, klass, symbols = N::PropertyUtils.resolve_prop_params(params)
280
+ symbol = symbols.first
281
+
282
+ N::PropertyUtils.enchant(self)
283
+
284
+ if self.is_a?(Class)
285
+
286
+ # Add some extra code to append features to
287
+ # subclasses.
288
+
289
+ self.module_eval %{
290
+ def self.inherited(sub)
291
+ N::PropertyUtils.enchant(sub)
292
+ N::PropertyUtils.copy_props(self, sub)
293
+ # gmosx: We have to define @@__props first to avoid reusing
294
+ # the hash from the module. super must stay at the end.
295
+ super
296
+ end
297
+ }
298
+ else
299
+
300
+ # Add some extra code for modules to append
301
+ # their features to classes that include it.
302
+
303
+ self.module_eval %{
304
+ def self.append_features(base)
305
+ N::PropertyUtils.enchant(base)
306
+ N::PropertyUtils.copy_props(self, base)
307
+
308
+ # gmosx: We have to define @@__props first to avoid reusing
309
+ # the hash from the module. super must stay at the end.
310
+
311
+ N::PropertyUtils.include_meta_mixins(base)
312
+
313
+ super
314
+ end
315
+ }
316
+ end
317
+
318
+ property = N::Property.new(symbol, klass, meta)
319
+
320
+ reader = meta[:reader] || true
321
+ writer = writer || meta[:writer] || false
322
+
323
+ meta[:reader] = true if meta[:reader].nil?
324
+ if defined?(writer)
325
+ meta[:writer] = writer
326
+ else
327
+ meta[:writer] = true if meta[:writer].nil?
328
+ end
329
+
330
+ N::PropertyUtils.add_prop(self, property)
331
+
332
+ # gmosx: should be placed AFTER enchant!
333
+
334
+ N::PropertyUtils.include_meta_mixins(self)
335
+ end
336
+
337
+ # Helper method. Accepts a collection of symbols and generates
338
+ # properties. Only generates reader.
339
+ #
340
+ # Example:
341
+ # prop_reader String, :name, :title, :body, :sql => "char(32)"
342
+
343
+ def prop_reader(*params)
344
+ meta, klass, symbols = N::PropertyUtils.resolve_prop_params(params)
345
+
346
+ meta[:reader] = true
347
+ meta[:writer] = false
348
+
349
+ for symbol in symbols
350
+ prop(klass, symbol, meta)
351
+ end
352
+ end
353
+
354
+ # Helper method. Accepts a collection of symbols and generates
355
+ # properties. Only generates writer.
356
+ #
357
+ # Example:
358
+ # prop_writer String, :name, :title, :body, :sql => "char(32)"
359
+
360
+ def prop_writer(*params)
361
+ meta, klass, symbols = N::PropertyUtils.resolve_prop_params(params)
362
+
363
+ meta[:reader] = false
364
+ meta[:writer] = true
365
+
366
+ for symbol in symbols
367
+ prop(klass, symbol, meta)
368
+ end
369
+ end
370
+
371
+ # Helper method. Accepts a collection of symbols and generates
372
+ # properties. Generates reader and writer.
373
+ #
374
+ # Example:
375
+ # prop_accessor String, :name, :title, :body, :sql => "char(32)"
376
+
377
+ def prop_accessor(*params)
378
+ meta, klass, symbols = N::PropertyUtils.resolve_prop_params(params)
379
+
380
+ meta[:reader] = true
381
+ meta[:writer] = true
382
+
383
+ for symbol in symbols
384
+ prop(klass, symbol, meta)
385
+ end
386
+ end
387
+ alias_method :property, :prop_accessor
388
+
389
+ # Attach metadata.
390
+ # Guard against duplicates, no need to keep order.
391
+ # This method uses closures :)
392
+ #--
393
+ # gmosx: crappy implementation, recode.
394
+ #++
395
+
396
+ def meta(key, *val)
397
+ val = val.first if val.size == 1
398
+
399
+ N::PropertyUtils.enchant(self)
400
+
401
+ self.module_eval %{
402
+ @@__meta[key] ||= []
403
+ @@__meta[key].delete_if { |v| val == v }
404
+ @@__meta[key] << val
405
+ }
406
+ end
407
+
408
+ end