glue 0.0.1 → 0.13.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.
@@ -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