glue 0.0.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +19 -0
- data/INSTALL +56 -0
- data/README +3 -0
- data/Rakefile +73 -10
- data/doc/AUTHORS +16 -0
- data/doc/LICENSE +33 -0
- data/doc/RELEASES +5 -0
- data/install.rb +47 -0
- data/lib/glue.rb +57 -59
- data/lib/glue/array.rb +61 -0
- data/lib/glue/attribute.rb +83 -0
- data/lib/glue/cache.rb +138 -0
- data/lib/glue/flexob.rb +12 -0
- data/lib/glue/hash.rb +122 -0
- data/lib/glue/inflector.rb +91 -0
- data/lib/glue/logger.rb +147 -0
- data/lib/glue/misc.rb +14 -0
- data/lib/glue/mixins.rb +36 -0
- data/lib/glue/number.rb +24 -0
- data/lib/glue/object.rb +32 -0
- data/lib/glue/pool.rb +60 -0
- data/lib/glue/property.rb +408 -0
- data/lib/glue/string.rb +162 -0
- data/lib/glue/time.rb +85 -0
- data/lib/glue/validation.rb +461 -0
- data/test/glue/tc_attribute.rb +22 -0
- data/test/glue/tc_cache.rb +45 -0
- data/test/glue/tc_hash.rb +38 -0
- data/test/glue/tc_logger.rb +39 -0
- data/test/glue/tc_numbers.rb +20 -0
- data/test/glue/tc_property.rb +91 -0
- data/test/glue/tc_property_mixins.rb +93 -0
- data/test/glue/tc_property_type_checking.rb +35 -0
- data/test/glue/tc_strings.rb +103 -0
- data/test/glue/tc_validation.rb +214 -0
- metadata +95 -89
- data/History.txt +0 -6
- data/Manifest.txt +0 -7
- data/README.txt +0 -127
- data/bin/glue +0 -1
- data/test/test_glue.rb +0 -218
@@ -0,0 +1,83 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# Original code from Rails distribution.
|
3
|
+
# http://www.rubyonrails.com
|
4
|
+
# $Id$
|
5
|
+
|
6
|
+
#--
|
7
|
+
# Extends the module object with module and instance accessors
|
8
|
+
# for class attributes, just like the native attr* accessors for
|
9
|
+
# instance attributes. Aliases for classes are also provided.
|
10
|
+
#
|
11
|
+
# Example:
|
12
|
+
#
|
13
|
+
# mattr_accessor :my_attr, 'Default value'
|
14
|
+
#++
|
15
|
+
class Module # :nodoc:
|
16
|
+
|
17
|
+
def mattr_reader(*params)
|
18
|
+
default = if params.last.is_a?(Symbol) then nil else params.pop end
|
19
|
+
|
20
|
+
|
21
|
+
for sym in params
|
22
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
23
|
+
|
24
|
+
if not defined?(@@#{sym.id2name})
|
25
|
+
@@#{sym.id2name} = #{default.inspect}
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.#{sym.id2name}
|
29
|
+
@@#{sym}
|
30
|
+
end
|
31
|
+
|
32
|
+
def #{sym.id2name}
|
33
|
+
@@#{sym}
|
34
|
+
end
|
35
|
+
|
36
|
+
def call_#{sym.id2name}
|
37
|
+
case @@#{sym.id2name}
|
38
|
+
when Symbol then send(@@#{sym})
|
39
|
+
when Proc then @@#{sym}.call(self)
|
40
|
+
when String then @@#{sym}
|
41
|
+
else nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end_eval
|
46
|
+
end
|
47
|
+
end
|
48
|
+
alias_method :cattr_reader, :mattr_reader
|
49
|
+
|
50
|
+
def mattr_writer(*params)
|
51
|
+
default = if params.last.is_a?(Symbol) then nil else params.pop end
|
52
|
+
|
53
|
+
for sym in params
|
54
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
55
|
+
|
56
|
+
if not defined?(@@#{sym.id2name})
|
57
|
+
@@#{sym.id2name} = #{default.inspect.inspect}
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.#{sym.id2name}=(obj)
|
61
|
+
@@#{sym.id2name} = obj
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.set_#{sym.id2name}(obj)
|
65
|
+
@@#{sym.id2name} = obj
|
66
|
+
end
|
67
|
+
|
68
|
+
def #{sym.id2name}=(obj)
|
69
|
+
@@#{sym} = obj
|
70
|
+
end
|
71
|
+
|
72
|
+
end_eval
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias_method :cattr_writer, :cattr_writer
|
76
|
+
|
77
|
+
def mattr_accessor(*syms)
|
78
|
+
mattr_reader(*syms)
|
79
|
+
mattr_writer(*syms)
|
80
|
+
end
|
81
|
+
alias_method :cattr_accessor, :mattr_accessor
|
82
|
+
|
83
|
+
end
|
data/lib/glue/cache.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# * Anastasios Koutoumanos <ak@navel.gr>
|
3
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
4
|
+
# $Id: cache.rb 282 2005-03-10 12:24:53Z gmosx $
|
5
|
+
|
6
|
+
module N
|
7
|
+
|
8
|
+
# A cache utilizing a simple LRU (Least Recently Used) policy.
|
9
|
+
# The items managed by this cache must respond to the #key method.
|
10
|
+
# Attempts to optimize reads rather than inserts!
|
11
|
+
#
|
12
|
+
# LRU semantics are enforced by inserting the items in a queue.
|
13
|
+
# The lru item is always at the tail. Two special sentinels
|
14
|
+
# (head, tail) are used to simplify (?) the code.
|
15
|
+
|
16
|
+
class LRUCache < Hash
|
17
|
+
|
18
|
+
# Mix this in your class to make LRU-managable.
|
19
|
+
|
20
|
+
module Item
|
21
|
+
attr_accessor :lru_key, :lru_prev, :lru_next
|
22
|
+
end
|
23
|
+
|
24
|
+
# head-tail sentinels
|
25
|
+
|
26
|
+
class Sentinel; include Item; end
|
27
|
+
|
28
|
+
# the maximum number of items in the cache.
|
29
|
+
|
30
|
+
attr_accessor :max_items
|
31
|
+
|
32
|
+
# the head sentinel
|
33
|
+
|
34
|
+
attr :head
|
35
|
+
|
36
|
+
# the tail sentinel, tail.prev points to the lru item.
|
37
|
+
|
38
|
+
attr :tail
|
39
|
+
|
40
|
+
def initialize(max_items)
|
41
|
+
@max_items = max_items
|
42
|
+
lru_clear()
|
43
|
+
end
|
44
|
+
|
45
|
+
# Lookup an item in the cache.
|
46
|
+
|
47
|
+
def [](key)
|
48
|
+
if item = super
|
49
|
+
return lru_touch(item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# The inserted item is considered mru!
|
54
|
+
|
55
|
+
def []=(key, item)
|
56
|
+
item = super
|
57
|
+
item.lru_key = key
|
58
|
+
lru_insert(item)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Delete an item from the cache.
|
62
|
+
|
63
|
+
def delete(key)
|
64
|
+
if item = super
|
65
|
+
lru_delete(item)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Clear the cache.
|
70
|
+
|
71
|
+
def clear
|
72
|
+
super
|
73
|
+
lru_clear()
|
74
|
+
end
|
75
|
+
|
76
|
+
# The first (mru) element in the cache.
|
77
|
+
|
78
|
+
def first
|
79
|
+
@head.lru_next
|
80
|
+
end
|
81
|
+
|
82
|
+
# The last (lru) element in the cache.
|
83
|
+
|
84
|
+
def last
|
85
|
+
@tail.lru_prev
|
86
|
+
end
|
87
|
+
alias_method :lru, :last
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Delete an item from the lru list.
|
92
|
+
|
93
|
+
def lru_delete(item)
|
94
|
+
lru_join(item.lru_prev, item.lru_next)
|
95
|
+
return item
|
96
|
+
end
|
97
|
+
|
98
|
+
# Join two items in the lru list.
|
99
|
+
# Return y to allow for chaining.
|
100
|
+
|
101
|
+
def lru_join(x, y)
|
102
|
+
x.lru_next = y
|
103
|
+
y.lru_prev = x
|
104
|
+
return y
|
105
|
+
end
|
106
|
+
|
107
|
+
# Append a child item to a parent item in the lru list
|
108
|
+
# (Re)inserts the child in the list.
|
109
|
+
|
110
|
+
def lru_append(parent, child)
|
111
|
+
lru_join(child, parent.lru_next)
|
112
|
+
lru_join(parent, child)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Insert an item
|
116
|
+
|
117
|
+
def lru_insert(item)
|
118
|
+
delete(last.lru_key) if size() > @max_items
|
119
|
+
lru_append(@head, item)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Touch an item, make mru!
|
123
|
+
# Returns the item.
|
124
|
+
|
125
|
+
def lru_touch(item)
|
126
|
+
lru_append(@head, lru_delete(item))
|
127
|
+
end
|
128
|
+
|
129
|
+
# Clear the lru.
|
130
|
+
|
131
|
+
def lru_clear
|
132
|
+
@head = Sentinel.new
|
133
|
+
@tail = Sentinel.new
|
134
|
+
lru_join(@head, @tail)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
data/lib/glue/flexob.rb
ADDED
data/lib/glue/hash.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id: hash.rb 282 2005-03-10 12:24:53Z gmosx $
|
4
|
+
|
5
|
+
require 'sync'
|
6
|
+
|
7
|
+
module N
|
8
|
+
|
9
|
+
# A thread-safe hash. We use a sync object instead of a mutex,
|
10
|
+
# because it is re-entrant. An exclusive lock is needed when
|
11
|
+
# writing, a shared lock IS NEEDED when reading
|
12
|
+
# uses the delegator pattern to allow for multiple
|
13
|
+
# implementations!
|
14
|
+
|
15
|
+
class SafeHash < Hash
|
16
|
+
attr :sync
|
17
|
+
|
18
|
+
# gmosx: delegator is not used.
|
19
|
+
#
|
20
|
+
def initialize(delegator = nil)
|
21
|
+
@sync = ::Sync.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
@sync.synchronize(::Sync::SH) { super }
|
26
|
+
end
|
27
|
+
|
28
|
+
def []=(key, value)
|
29
|
+
@sync.synchronize(::Sync::EX) { super }
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete(key)
|
33
|
+
@sync.synchronize(::Sync::EX) { super }
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear
|
37
|
+
@sync.synchronize(::Sync::EX) { super }
|
38
|
+
end
|
39
|
+
|
40
|
+
def size
|
41
|
+
@sync.synchronize(::Sync::SH) { super }
|
42
|
+
end
|
43
|
+
|
44
|
+
def values
|
45
|
+
@sync.synchronize(::Sync::SH) { super }
|
46
|
+
end
|
47
|
+
|
48
|
+
def keys
|
49
|
+
@sync.synchronize(::Sync::SH) { super }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# A thread-safe hash. We use a sync object instead of a mutex,
|
55
|
+
# because it is re-entrant. An exclusive lock is needed when
|
56
|
+
# writing, a shared lock IS NEEDED when reading.
|
57
|
+
#
|
58
|
+
# === Design
|
59
|
+
#
|
60
|
+
# This class uses the delegator pattern. However we dont use rubys
|
61
|
+
# delegation facilities, they are more general and powerfull than we
|
62
|
+
# need here (and slower). Instead a custom (but simple) solution is
|
63
|
+
# used.
|
64
|
+
#
|
65
|
+
# === Example
|
66
|
+
#
|
67
|
+
# hash = SafeHashDelegator.new(Hash.new)
|
68
|
+
# hash = SafeHashDelegator.new(Hash.new)
|
69
|
+
|
70
|
+
class SafeHashDelegator < Hash
|
71
|
+
attr :delegate, :sync
|
72
|
+
|
73
|
+
def initialize(delegate)
|
74
|
+
@delegate = delegate
|
75
|
+
@sync = ::Sync.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def [](key)
|
79
|
+
@sync.synchronize(::Sync::SH) {
|
80
|
+
@delegate[key]
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def []=(key, value)
|
85
|
+
@sync.synchronize(::Sync::EX) {
|
86
|
+
@delegate[key] = value
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def delete(key)
|
91
|
+
@sync.synchronize(::Sync::EX) {
|
92
|
+
@delegate.delete(key)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def clear
|
97
|
+
@sync.synchronize(::Sync::EX) {
|
98
|
+
@delegate.clear
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def size
|
103
|
+
@sync.synchronize(::Sync::SH) {
|
104
|
+
@delegate.size()
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def values
|
109
|
+
@sync.synchronize(::Sync::SH) {
|
110
|
+
@delegate.values()
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def keys
|
115
|
+
@sync.synchronize(::Sync::SH) {
|
116
|
+
@delegate.keys()
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Code from RubyOnRails (http://www.rubyonrails.com)
|
2
|
+
# Copyright (c) 2004 David Heinemeier Hansson.
|
3
|
+
|
4
|
+
module N
|
5
|
+
|
6
|
+
# The Inflector transforms words from singular to plural,
|
7
|
+
# class names to table names, modulized class names to ones without,
|
8
|
+
# and class names to foreign keys.
|
9
|
+
|
10
|
+
module Inflector
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def pluralize(word)
|
14
|
+
result = word.dup
|
15
|
+
plural_rules.each do |(rule, replacement)|
|
16
|
+
break if result.gsub!(rule, replacement)
|
17
|
+
end
|
18
|
+
return result
|
19
|
+
end
|
20
|
+
|
21
|
+
def singularize(word)
|
22
|
+
result = word.dup
|
23
|
+
singular_rules.each do |(rule, replacement)|
|
24
|
+
break if result.gsub!(rule, replacement)
|
25
|
+
end
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
|
29
|
+
def camelize(lower_case_and_underscored_word)
|
30
|
+
lower_case_and_underscored_word.gsub(/(^|_)(.)/){$2.upcase}
|
31
|
+
end
|
32
|
+
|
33
|
+
def underscore(camel_cased_word)
|
34
|
+
camel_cased_word.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def demodulize(class_name_in_module)
|
38
|
+
class_name_in_module.gsub(/^.*::/, '')
|
39
|
+
end
|
40
|
+
|
41
|
+
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
|
42
|
+
Inflector.underscore(Inflector.demodulize(class_name)) +
|
43
|
+
(separate_class_name_and_id_with_underscore ? "_id" : "id")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convert a class to a name.
|
47
|
+
|
48
|
+
def name(klass)
|
49
|
+
Inflector.underscore(Inflector.demodulize(klass.to_s))
|
50
|
+
end
|
51
|
+
|
52
|
+
# Convert a class to a name in plural
|
53
|
+
|
54
|
+
def plural_name(klass)
|
55
|
+
Inflector.pluralize(Inflector.underscore(Inflector.demodulize(klass.to_s)))
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def plural_rules #:doc:
|
60
|
+
[
|
61
|
+
[/(x|ch|ss)$/, '\1es'], # search, switch, fix, box, process, address
|
62
|
+
[/([^aeiouy]|qu)y$/, '\1ies'], # query, ability, agency
|
63
|
+
[/(?:([^f])fe|([lr])f)$/, '\1\2ves'], # half, safe, wife
|
64
|
+
[/sis$/, 'ses'], # basis, diagnosis
|
65
|
+
[/([ti])um$/, '\1a'], # datum, medium
|
66
|
+
[/person$/, 'people'], # person, salesperson
|
67
|
+
[/man$/, 'men'], # man, woman, spokesman
|
68
|
+
[/child$/, 'children'], # child
|
69
|
+
[/s$/, 's'], # no change (compatibility)
|
70
|
+
[/$/, 's']
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
74
|
+
def singular_rules #:doc:
|
75
|
+
[
|
76
|
+
[/(x|ch|ss)es$/, '\1'],
|
77
|
+
[/([^aeiouy]|qu)ies$/, '\1y'],
|
78
|
+
[/([lr])ves$/, '\1f'],
|
79
|
+
[/([^f])ves$/, '\1fe'],
|
80
|
+
[/(analy|ba|diagno|parenthe|progno|synop|the)ses$/, '\1sis'],
|
81
|
+
[/([ti])a$/, '\1um'],
|
82
|
+
[/people$/, 'person'],
|
83
|
+
[/men$/, 'man'],
|
84
|
+
[/status$/, 'status'],
|
85
|
+
[/children$/, 'child'],
|
86
|
+
[/s$/, '']
|
87
|
+
]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|