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/autoreload.rb
CHANGED
data/lib/glue/builder.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Glue
|
2
|
+
|
3
|
+
# A Builder integrates a number of Modules containing text
|
4
|
+
# manipulation utilities and provides an alternative
|
5
|
+
# 'accomulation' interface.
|
6
|
+
|
7
|
+
class Builder
|
8
|
+
|
9
|
+
# The builder output is accomulated in the buffer.
|
10
|
+
|
11
|
+
attr_accessor :buffer
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def include_builder(*modules)
|
16
|
+
for mod in modules
|
17
|
+
include mod
|
18
|
+
for meth in mod.public_instance_methods
|
19
|
+
self.module_eval %{
|
20
|
+
alias_method :_mixin_#{meth}, :#{meth}
|
21
|
+
def #{meth}(*args)
|
22
|
+
@buffer << _mixin_#{meth}(*args)
|
23
|
+
return self
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
alias_method :builder, :include_builder
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
# Provide the target where the builder output will be
|
34
|
+
# accomulated. The builder utilizes duck typing to make it
|
35
|
+
# compatible with anly target responding to <<.
|
36
|
+
|
37
|
+
def initialize(buffer = '', *modules)
|
38
|
+
@buffer = buffer
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# * George Moschovitis <gm@navel.gr>
|
47
|
+
|
48
|
+
__END__
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'glue/builder'
|
2
|
+
|
3
|
+
require 'nitro/mixin/xhtml'
|
4
|
+
require 'nitro/mixin/form'
|
5
|
+
require 'nitro/mixin/table'
|
6
|
+
|
7
|
+
module Glue
|
8
|
+
|
9
|
+
# A Builder for programmatically building XML blocks.
|
10
|
+
#--
|
11
|
+
# TODO: move to nitro or move mixins here.
|
12
|
+
#++
|
13
|
+
|
14
|
+
class XmlBuilder < Builder
|
15
|
+
include_builder Nitro::XhtmlMixin
|
16
|
+
include_builder Nitro::TableMixin
|
17
|
+
include_builder Nitro::FormMixin
|
18
|
+
|
19
|
+
def method_missing(tag, *args, &block)
|
20
|
+
self.class.module_eval <<-"end_eval", __FILE__, __LINE__
|
21
|
+
def #{tag}(*args)
|
22
|
+
attrs = args.last.is_a?(Hash) ? args.pop : nil
|
23
|
+
|
24
|
+
if block_given?
|
25
|
+
start_tag!('#{tag}', attrs)
|
26
|
+
yield
|
27
|
+
end_tag!('#{tag}')
|
28
|
+
elsif (!args.empty?)
|
29
|
+
start_tag!('#{tag}', attrs)
|
30
|
+
@buffer << args.first
|
31
|
+
end_tag!('#{tag}')
|
32
|
+
else
|
33
|
+
start_tag!('#{tag}', attrs, false)
|
34
|
+
@buffer << ' />'
|
35
|
+
end
|
36
|
+
|
37
|
+
return self
|
38
|
+
end
|
39
|
+
end_eval
|
40
|
+
|
41
|
+
self.send(tag, *args, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Emit the start (opening) tag of an element.
|
45
|
+
|
46
|
+
def start_tag!(tag, attributes = nil, close = true)
|
47
|
+
unless attributes
|
48
|
+
if close
|
49
|
+
@buffer << "<#{tag}>"
|
50
|
+
else
|
51
|
+
@buffer << "<#{tag}"
|
52
|
+
end
|
53
|
+
else
|
54
|
+
@buffer << "<#{tag}"
|
55
|
+
for name, value in attributes
|
56
|
+
if value
|
57
|
+
@buffer << %| #{name}="#{value}"|
|
58
|
+
else
|
59
|
+
@buffer << %| #{name}="1"|
|
60
|
+
end
|
61
|
+
end
|
62
|
+
@buffer << ">" if close
|
63
|
+
end
|
64
|
+
|
65
|
+
return self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Emit the end (closing) tag of an element.
|
69
|
+
|
70
|
+
def end_tag!(tag)
|
71
|
+
@buffer << "</#{tag}>"
|
72
|
+
|
73
|
+
return self
|
74
|
+
end
|
75
|
+
|
76
|
+
# Emit a text string.
|
77
|
+
|
78
|
+
def text!(str)
|
79
|
+
@buffer << str
|
80
|
+
|
81
|
+
return self
|
82
|
+
end
|
83
|
+
alias_method :print, :text!
|
84
|
+
|
85
|
+
# Emit a comment.
|
86
|
+
|
87
|
+
def comment!(str)
|
88
|
+
@buffer << "<!-- #{str} -->"
|
89
|
+
|
90
|
+
return self
|
91
|
+
end
|
92
|
+
|
93
|
+
# Emit a processing instruction.
|
94
|
+
|
95
|
+
def processing_instruction!(name, attributes = nil)
|
96
|
+
unless attributes
|
97
|
+
@buffer << "<?#{name} ?>"
|
98
|
+
else
|
99
|
+
@buffer << "<?#{name} "
|
100
|
+
attributes.each do |a, v|
|
101
|
+
@buffer << %[#{a}="#{v}" ]
|
102
|
+
end
|
103
|
+
@buffer << "?>"
|
104
|
+
end
|
105
|
+
|
106
|
+
return self
|
107
|
+
end
|
108
|
+
alias_method :pi!, :processing_instruction!
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/glue/cache.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
unless Enumerable.instance_methods.include?("min_by")
|
2
|
+
# for Ruby 1.8
|
3
|
+
module Enumerable
|
4
|
+
def min_by(&block)
|
5
|
+
min {|i,j| block.call(i) <=> block.call(j) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Glue
|
11
|
+
|
12
|
+
# Abstract super class of all cache implementations.
|
13
|
+
class Cache; end
|
14
|
+
|
15
|
+
# Abstract super class of all caching strategies.
|
16
|
+
class Cache::Strategy; end
|
17
|
+
|
18
|
+
# Implements an unbounded cache strategy. The cache size can
|
19
|
+
# grow to infinity.
|
20
|
+
|
21
|
+
class Cache::Strategy::Unbounded < Cache::Strategy
|
22
|
+
class Item < Struct.new(:value); end
|
23
|
+
def item_class() Item end
|
24
|
+
|
25
|
+
def access(item) end
|
26
|
+
def delete(item) end
|
27
|
+
def insert_or_extrude(item, enum) end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Abstract class for a capacity bounded strategy. Only up to
|
31
|
+
# _capacity_ items are allowed to be stored in the cache
|
32
|
+
# at any time.
|
33
|
+
|
34
|
+
class Cache::Strategy::CapacityBounded < Cache::Strategy
|
35
|
+
attr_accessor :capacity
|
36
|
+
|
37
|
+
def initialize(capacity)
|
38
|
+
@capacity = capacity
|
39
|
+
@n_items = 0 # number of items in cache
|
40
|
+
end
|
41
|
+
|
42
|
+
def inc(item)
|
43
|
+
raise if full?
|
44
|
+
@n_items += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def dec(item)
|
48
|
+
raise if empty?
|
49
|
+
@n_items -= 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def full?
|
53
|
+
@n_items >= @capacity
|
54
|
+
end
|
55
|
+
|
56
|
+
def empty?
|
57
|
+
@n_items == 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Implements the least frequently used (LFU) strategy.
|
62
|
+
|
63
|
+
class Cache::Strategy::LFU < Cache::Strategy::CapacityBounded
|
64
|
+
class Item < Struct.new(:value, :freq); end
|
65
|
+
def item_class() Item end
|
66
|
+
|
67
|
+
def access(item)
|
68
|
+
item.freq += 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete(item)
|
72
|
+
dec(item)
|
73
|
+
end
|
74
|
+
|
75
|
+
# enum::
|
76
|
+
# a [key, item] enumerable
|
77
|
+
|
78
|
+
def insert_or_extrude(item, enum)
|
79
|
+
# find least recently used key/item and yield
|
80
|
+
yield enum.min_by {|key, it| it.freq} while full?
|
81
|
+
item.freq = 0
|
82
|
+
inc(item)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Implements the least recently used (LRU) strategy.
|
87
|
+
|
88
|
+
class Cache::Strategy::LRU < Cache::Strategy::CapacityBounded
|
89
|
+
class Item < Struct.new(:value, :time); end
|
90
|
+
def item_class() Item end
|
91
|
+
|
92
|
+
def access(item)
|
93
|
+
item.time = Time.now
|
94
|
+
end
|
95
|
+
|
96
|
+
def delete(item)
|
97
|
+
dec(item)
|
98
|
+
end
|
99
|
+
|
100
|
+
# enum::
|
101
|
+
# a [key, item] enumerable
|
102
|
+
#
|
103
|
+
def insert_or_extrude(item, enum)
|
104
|
+
# find least recently used key/item and yield
|
105
|
+
yield enum.min_by {|key, it| it.time} while full?
|
106
|
+
item.time = Time.now
|
107
|
+
inc(item)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Implements a cache using a parameterizable strategy and a storage.
|
112
|
+
# The protocol that the _store_ must understand is:
|
113
|
+
#
|
114
|
+
# fetch(key) -> val
|
115
|
+
# has_key?(key)
|
116
|
+
# delete(key) -> val
|
117
|
+
# each {|key, val| }
|
118
|
+
|
119
|
+
class Cache::StorageCache < Cache
|
120
|
+
|
121
|
+
def initialize(strategy, store=Hash.new, store_on_update=false)
|
122
|
+
@strategy = strategy
|
123
|
+
@store = store
|
124
|
+
@store_on_update = store_on_update
|
125
|
+
end
|
126
|
+
|
127
|
+
def has_key?(key)
|
128
|
+
@store.has_key?(key)
|
129
|
+
end
|
130
|
+
|
131
|
+
def delete(key)
|
132
|
+
if @store.has_key?(key)
|
133
|
+
item = @store.delete(key)
|
134
|
+
@strategy.delete(item)
|
135
|
+
item.value
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def fetch(key, default_value=nil)
|
142
|
+
if @store.has_key?(key)
|
143
|
+
item = @store.fetch(key)
|
144
|
+
@strategy.access(item)
|
145
|
+
@store.store(key, item) if @store_on_update
|
146
|
+
item.value
|
147
|
+
else
|
148
|
+
default_value
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def store(key, value)
|
153
|
+
if @store.has_key?(key)
|
154
|
+
# update only
|
155
|
+
item = @store.fetch(key)
|
156
|
+
item.value = value
|
157
|
+
@strategy.access(item)
|
158
|
+
@store.store(key, item) if @store_on_update
|
159
|
+
else
|
160
|
+
# insert new item
|
161
|
+
item = @strategy.item_class.new
|
162
|
+
item.value = value
|
163
|
+
@strategy.insert_or_extrude(item, @store) do |k, i|
|
164
|
+
@strategy.delete(i)
|
165
|
+
@store.delete(k)
|
166
|
+
end
|
167
|
+
@store.store(key, item) # correct!
|
168
|
+
end
|
169
|
+
value
|
170
|
+
end
|
171
|
+
|
172
|
+
alias [] fetch
|
173
|
+
alias []= store
|
174
|
+
end
|
175
|
+
|
176
|
+
# Implements an in-memory cache (uses a Hash as store) with a LRU replacement
|
177
|
+
# stragegy.
|
178
|
+
|
179
|
+
class Cache::MemoryLRUCache < Cache::StorageCache
|
180
|
+
DEFAULT_CAPACITY = 20
|
181
|
+
|
182
|
+
def initialize(capacity=DEFAULT_CAPACITY)
|
183
|
+
super(Cache::Strategy::LRU.new(capacity))
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end # module Glue
|
188
|
+
|
189
|
+
# * Michael Neumann <mneumann@ntecs.de>
|
data/lib/glue/configuration.rb
CHANGED
@@ -14,87 +14,105 @@ require 'glue/flexob'
|
|
14
14
|
|
15
15
|
class Configuration
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
# A hash of setting owners. Use double @'s to allow for
|
18
|
+
# the Settings alias.
|
19
|
+
#--
|
20
|
+
# TODO: find a better name.
|
21
|
+
#++
|
22
|
+
|
23
|
+
@@owners = Glue::SafeHash.new
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
25
|
+
# A datastructure to store Settings metadata.
|
26
|
+
|
27
|
+
class Setting
|
28
|
+
attr_accessor :owner, :name, :type, :value, :options
|
29
|
+
|
30
|
+
def initialize(owner, name, options)
|
31
|
+
raise ArgumentError.new('A default value is required') unless options.key?(:default)
|
32
|
+
@owner, @name = owner, name
|
33
|
+
@options = options
|
34
|
+
@value = options[:default]
|
35
|
+
@type = options[:type] = options[:type] || @value.class
|
36
|
+
end
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
def value=(value)
|
39
|
+
@value = value
|
40
|
+
constant(@owner).module_eval %{
|
41
|
+
@@#{@name} = #{value.inspect}
|
42
|
+
}
|
43
|
+
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
45
|
+
def to_s
|
46
|
+
@value.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class << self
|
51
|
+
|
52
|
+
# Inject the configuration parameters to configuration
|
53
|
+
# classes.
|
54
|
+
|
55
|
+
def setup(options)
|
56
|
+
options.each do |owner, ss|
|
57
|
+
next unless ss
|
58
|
+
begin
|
59
|
+
owner = constant(owner)
|
60
|
+
rescue NameError
|
61
|
+
next
|
62
|
+
end
|
63
|
+
ss.each do |name, s|
|
64
|
+
@@owners[owner][name.to_sym].value = s
|
65
|
+
owner.module_eval %{
|
66
|
+
@@#{name} = #{s.inspect}
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
63
71
|
|
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
|
-
|
72
|
+
# Parse configuration parameters in yaml format.
|
73
|
+
|
74
|
+
def parse(options)
|
75
|
+
temp = YAML::load(options)
|
76
|
+
options = {}
|
77
|
+
temp.each do |k, v|
|
78
|
+
begin
|
79
|
+
options[constant(k.gsub(/\./, '::').to_sym)] = v
|
80
|
+
rescue Object
|
81
|
+
options[k] = v
|
82
|
+
end
|
83
|
+
end
|
84
|
+
setup(options)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Load and parse an external yaml configuration file.
|
88
|
+
|
89
|
+
def load(filename)
|
90
|
+
parse(File.read(filename))
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_setting(owner, name, options)
|
94
|
+
s = @@owners[owner] || {}
|
95
|
+
s[name] = Setting.new(owner, name, options)
|
96
|
+
@@owners[owner] = s
|
97
|
+
end
|
98
|
+
|
99
|
+
def settings(owner = nil)
|
100
|
+
if owner
|
101
|
+
@@owners[owner]
|
102
|
+
else
|
103
|
+
@@owners.values.inject([]) { |memo, obj| memo.concat(obj.values) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
alias_method :all, :settings
|
107
|
+
alias_method :[], :settings
|
90
108
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
109
|
+
def method_missing(sym)
|
110
|
+
if sym.to_s =~ /[A-Z]/ # facet's capitalized? is buggy at the moment.
|
111
|
+
Flexob.new(self[constant(sym)])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
98
116
|
end
|
99
117
|
|
100
118
|
# Alias for the Configuration class (shorter).
|
@@ -104,23 +122,23 @@ end
|
|
104
122
|
|
105
123
|
class Module
|
106
124
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
125
|
+
# Defines a configuration setting.
|
126
|
+
#--
|
127
|
+
# TODO: implement with annotations.
|
128
|
+
#++
|
129
|
+
|
130
|
+
def setting(sym, options = {})
|
131
|
+
Configuration.add_setting(self, sym, options)
|
132
|
+
module_eval %{
|
133
|
+
mattr_accessor sym, options[:default]
|
134
|
+
|
117
135
|
def self.#{sym.id2name}=(obj)
|
118
136
|
@@#{sym.id2name} = obj
|
119
137
|
Configuration[#{self}][:#{sym}].value = obj
|
120
138
|
end
|
121
|
-
|
122
|
-
|
123
|
-
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
124
142
|
end
|
125
143
|
|
126
|
-
# * George Moschovitis <gm@navel.gr>
|
144
|
+
# * George Moschovitis <gm@navel.gr>
|