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.
Files changed (58) hide show
  1. data/CHANGELOG +161 -110
  2. data/INSTALL +12 -12
  3. data/README +1 -1
  4. data/Rakefile +43 -45
  5. data/doc/AUTHORS +5 -5
  6. data/doc/LICENSE +3 -3
  7. data/doc/RELEASES +32 -24
  8. data/install.rb +7 -17
  9. data/lib/facet/object/alias_class.rb +12 -0
  10. data/lib/glue.rb +35 -35
  11. data/lib/glue/array.rb +46 -46
  12. data/lib/glue/aspects.rb +199 -209
  13. data/lib/glue/attribute.rb +15 -15
  14. data/lib/glue/autoreload.rb +1 -1
  15. data/lib/glue/builder.rb +48 -0
  16. data/lib/glue/builder/xml.rb +114 -0
  17. data/lib/glue/cache.rb +189 -0
  18. data/lib/glue/configuration.rb +108 -90
  19. data/lib/glue/flexob.rb +17 -17
  20. data/lib/glue/hash.rb +71 -71
  21. data/lib/glue/helper.rb +12 -12
  22. data/lib/glue/idgen.rb +9 -0
  23. data/lib/glue/idgen/md5.rb +24 -0
  24. data/lib/glue/idgen/sequential.rb +15 -0
  25. data/lib/glue/literal_method.rb +44 -0
  26. data/lib/glue/localization.rb +130 -0
  27. data/lib/glue/logger.rb +98 -98
  28. data/lib/glue/misc.rb +7 -7
  29. data/lib/glue/mixins.rb +19 -19
  30. data/lib/glue/number.rb +8 -8
  31. data/lib/glue/object.rb +2 -2
  32. data/lib/glue/pool.rb +43 -43
  33. data/lib/glue/property.rb +392 -392
  34. data/lib/glue/sanitize.rb +34 -34
  35. data/lib/glue/settings.rb +1 -1
  36. data/lib/glue/snapshot.rb +104 -0
  37. data/lib/glue/string.rb +129 -129
  38. data/lib/glue/time.rb +53 -53
  39. data/lib/glue/uri.rb +162 -162
  40. data/lib/glue/validation.rb +421 -421
  41. data/lib/vendor/blankslate.rb +53 -0
  42. data/test/glue/builder/tc_xml.rb +56 -0
  43. data/test/glue/tc_aspects.rb +90 -90
  44. data/test/glue/tc_attribute.rb +11 -11
  45. data/test/glue/tc_builder.rb +30 -0
  46. data/test/glue/tc_configuration.rb +97 -97
  47. data/test/glue/tc_flexob.rb +10 -10
  48. data/test/glue/tc_hash.rb +23 -23
  49. data/test/glue/tc_localization.rb +49 -0
  50. data/test/glue/tc_logger.rb +31 -31
  51. data/test/glue/tc_numbers.rb +9 -9
  52. data/test/glue/tc_property.rb +67 -67
  53. data/test/glue/tc_property_mixins.rb +17 -17
  54. data/test/glue/tc_property_type_checking.rb +13 -13
  55. data/test/glue/tc_strings.rb +94 -94
  56. data/test/glue/tc_uri.rb +65 -65
  57. data/test/glue/tc_validation.rb +196 -196
  58. metadata +26 -4
@@ -24,7 +24,7 @@ def autoreload(check_interval=10)
24
24
  end
25
25
  end
26
26
  end
27
- }
27
+ }
28
28
  end
29
29
 
30
30
  end
@@ -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>
@@ -14,87 +14,105 @@ require 'glue/flexob'
14
14
 
15
15
  class Configuration
16
16
 
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
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
- # 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
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
- def value=(value)
39
- @value = value
40
- constant(@owner).module_eval %{
41
- @@#{@name} = #{value.inspect}
42
- }
43
- end
38
+ def value=(value)
39
+ @value = value
40
+ constant(@owner).module_eval %{
41
+ @@#{@name} = #{value.inspect}
42
+ }
43
+ end
44
44
 
45
- def to_s
46
- @value.to_s
47
- end
48
- end
49
-
50
- class << self
51
-
52
- def setup(options)
53
- options.each do |owner, ss|
54
- owner = constant(owner)
55
- ss.each do |name, s|
56
- @@owners[owner][name.to_sym].value = s
57
- owner.module_eval %{
58
- @@#{name} = #{s.inspect}
59
- }
60
- end
61
- end
62
- end
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
- def parse(options)
65
- temp = YAML::load(options)
66
- options = {}
67
- temp.each { |k, v| options[constant(k.gsub(/\./, '::').to_sym)] = v }
68
- setup(options)
69
- end
70
-
71
- def load(filename)
72
- parse(File.read(filename))
73
- end
74
-
75
- def add_setting(owner, name, options)
76
- s = @@owners[owner] || {}
77
- s[name] = Setting.new(owner, name, options)
78
- @@owners[owner] = s
79
- end
80
-
81
- def settings(owner = nil)
82
- if owner
83
- @@owners[owner]
84
- else
85
- @@owners.values.inject([]) { |memo, obj| memo.concat(obj.values) }
86
- end
87
- end
88
- alias_method :all, :settings
89
- alias_method :[], :settings
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
- def method_missing(sym)
92
- if sym.to_s =~ /[A-Z]/ # facet's capitalized? is buggy at the moment.
93
- Flexob.new(self[constant(sym)])
94
- end
95
- end
96
- end
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
- # Defines a configuration setting.
108
- #--
109
- # TODO: implement with annotations.
110
- #++
111
-
112
- def setting(sym, options = {})
113
- Configuration.add_setting(self, sym, options)
114
- module_eval %{
115
- mattr_accessor sym, options[:default]
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
- end
123
-
139
+ }
140
+ end
141
+
124
142
  end
125
143
 
126
- # * George Moschovitis <gm@navel.gr>
144
+ # * George Moschovitis <gm@navel.gr>