ruby_ex 0.1.1

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 (108) hide show
  1. data/AUTHORS +51 -0
  2. data/ChangeLog +1763 -0
  3. data/NEWS +3 -0
  4. data/README +1 -0
  5. data/Rakefile +8 -0
  6. data/SPEC.dyn.yml +10 -0
  7. data/SPEC.gem.yml +269 -0
  8. data/SPEC.yml +36 -0
  9. data/src/abstract.rb +253 -0
  10. data/src/abstract_node.rb +85 -0
  11. data/src/algorithms.rb +12 -0
  12. data/src/algorithms/simulated_annealing.rb +142 -0
  13. data/src/ask.rb +100 -0
  14. data/src/attributed_class.rb +303 -0
  15. data/src/cache.rb +350 -0
  16. data/src/checkout.rb +12 -0
  17. data/src/choose.rb +271 -0
  18. data/src/commands.rb +20 -0
  19. data/src/commands/command.rb +492 -0
  20. data/src/commands/datas.rb +16 -0
  21. data/src/commands/datas/composite.rb +31 -0
  22. data/src/commands/datas/data.rb +65 -0
  23. data/src/commands/datas/factory.rb +69 -0
  24. data/src/commands/datas/temp.rb +26 -0
  25. data/src/commands/factory.rb +67 -0
  26. data/src/commands/helpers.rb +81 -0
  27. data/src/commands/pipe.rb +66 -0
  28. data/src/commands/runners.rb +16 -0
  29. data/src/commands/runners/exec.rb +50 -0
  30. data/src/commands/runners/fork.rb +130 -0
  31. data/src/commands/runners/runner.rb +140 -0
  32. data/src/commands/runners/system.rb +57 -0
  33. data/src/commands/seq.rb +32 -0
  34. data/src/config_file.rb +95 -0
  35. data/src/const_regexp.rb +57 -0
  36. data/src/daemon.rb +135 -0
  37. data/src/diff.rb +665 -0
  38. data/src/dlogger.rb +62 -0
  39. data/src/drb/drb_observable.rb +95 -0
  40. data/src/drb/drb_observable_pool.rb +27 -0
  41. data/src/drb/drb_service.rb +44 -0
  42. data/src/drb/drb_undumped_attributes.rb +56 -0
  43. data/src/drb/drb_undumped_indexed_object.rb +55 -0
  44. data/src/drb/insecure_protected_methods.rb +101 -0
  45. data/src/drb_ex.rb +12 -0
  46. data/src/dumpable_proc.rb +57 -0
  47. data/src/filetype.rb +229 -0
  48. data/src/generate_id.rb +44 -0
  49. data/src/histogram.rb +222 -0
  50. data/src/hookable.rb +283 -0
  51. data/src/hooker.rb +54 -0
  52. data/src/indexed_node.rb +65 -0
  53. data/src/io_marshal.rb +99 -0
  54. data/src/ioo.rb +193 -0
  55. data/src/labeled_node.rb +62 -0
  56. data/src/logger_observer.rb +24 -0
  57. data/src/md5sum.rb +70 -0
  58. data/src/module/autoload_tree.rb +65 -0
  59. data/src/module/hierarchy.rb +334 -0
  60. data/src/module/instance_method_visibility.rb +71 -0
  61. data/src/node.rb +81 -0
  62. data/src/object_monitor.rb +143 -0
  63. data/src/object_monitor_activity.rb +34 -0
  64. data/src/observable.rb +138 -0
  65. data/src/observable_pool.rb +291 -0
  66. data/src/orderedhash.rb +252 -0
  67. data/src/pp_hierarchy.rb +30 -0
  68. data/src/random_generators.rb +29 -0
  69. data/src/random_generators/random_generator.rb +33 -0
  70. data/src/random_generators/ruby.rb +25 -0
  71. data/src/ruby_ex.rb +124 -0
  72. data/src/safe_eval.rb +346 -0
  73. data/src/sendmail.rb +214 -0
  74. data/src/service_manager.rb +122 -0
  75. data/src/shuffle.rb +30 -0
  76. data/src/spring.rb +134 -0
  77. data/src/spring_set.rb +134 -0
  78. data/src/symtbl.rb +108 -0
  79. data/src/synflow.rb +474 -0
  80. data/src/thread_mutex.rb +11 -0
  81. data/src/timeout_ex.rb +79 -0
  82. data/src/trace.rb +26 -0
  83. data/src/uri/druby.rb +78 -0
  84. data/src/uri/file.rb +63 -0
  85. data/src/uri/ftp_ex.rb +36 -0
  86. data/src/uri/http_ex.rb +41 -0
  87. data/src/uri/pgsql.rb +136 -0
  88. data/src/uri/ssh.rb +87 -0
  89. data/src/uri/svn.rb +113 -0
  90. data/src/uri_ex.rb +71 -0
  91. data/src/verbose_object.rb +70 -0
  92. data/src/yaml/basenode_ext.rb +63 -0
  93. data/src/yaml/chop_header.rb +24 -0
  94. data/src/yaml/transform.rb +450 -0
  95. data/src/yaml/yregexpath.rb +76 -0
  96. data/test/algorithms/simulated_annealing_test.rb +102 -0
  97. data/test/check-pkg-ruby_ex.yml +15 -0
  98. data/test/check-ruby_ex.yml +12 -0
  99. data/test/resources/autoload_tree/A.rb +11 -0
  100. data/test/resources/autoload_tree/B.rb +10 -0
  101. data/test/resources/autoload_tree/foo/C.rb +18 -0
  102. data/test/resources/foo.txt +6 -0
  103. data/test/sanity-suite.yml +12 -0
  104. data/test/sanity/multiple-requires.yml +20 -0
  105. data/test/sanity/single-requires.yml +24 -0
  106. data/test/test-unit-setup.rb +6 -0
  107. data/test/unit-suite.yml +14 -0
  108. metadata +269 -0
@@ -0,0 +1,85 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: abstract_node.rb 266 2005-06-01 14:27:18Z ertai $
7
+
8
+
9
+ require 'ruby_ex'
10
+ require 'abstract'
11
+
12
+ class AbstractNode
13
+ include Abstract
14
+
15
+ def initialize(data=nil, *sub_nodes)
16
+ @data = data
17
+ self.each_node { |sub_node| check_sub_node_type(sub_node) }
18
+ end
19
+
20
+ attr_reader :data, :sub_nodes
21
+
22
+ def [](index)
23
+ @sub_nodes[index]
24
+ end
25
+
26
+ def []=(index, sub_node)
27
+ check_sub_node_type(sub_node)
28
+ @sub_nodes[index] = sub_node
29
+ end
30
+
31
+ def merge!(sub_nodes)
32
+ sub_nodes.each { |index, sub_node| self[index] = sub_node }
33
+ end
34
+
35
+ def each_pair(&block)
36
+ @sub_nodes.each_pair { |index, sub_node| block[index, sub_node] }
37
+ end
38
+
39
+ def each_node(&block)
40
+ @sub_nodes.each_pair { |index, sub_node| block[sub_node] }
41
+ end
42
+
43
+ alias :each :each_node
44
+
45
+ def each_index(&block)
46
+ @sub_nodes.each_pair { |index, sub_node| block[index] }
47
+ end
48
+
49
+ def delete(index)
50
+ @sub_nodes.delete(index)
51
+ end
52
+
53
+ def nb_sub_nodes
54
+ @sub_nodes.size
55
+ end
56
+
57
+ alias size nb_sub_nodes
58
+ alias length nb_sub_nodes
59
+
60
+ def leaf?
61
+ @sub_nodes.empty?
62
+ end
63
+
64
+ def pre_depth_first(index=nil, &block)
65
+ block[index, self]
66
+ @sub_nodes.each_pair do |index, sub_node|
67
+ sub_node.pre_depth_first(index, &block)
68
+ end
69
+ nil
70
+ end
71
+
72
+ # FIXME: implement me
73
+ # def breadth_first(&block)
74
+ # end
75
+
76
+ protected
77
+ def check_sub_node_type(sub_node)
78
+ unless sub_node.is_a?(self.class)
79
+ raise(TypeError, "`#{sub_node}' - must be a #{self.class}")
80
+ end
81
+ end
82
+
83
+ end # class AbstractNode
84
+
85
+
data/src/algorithms.rb ADDED
@@ -0,0 +1,12 @@
1
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License:: Gnu General Public License.
4
+ # Revision:: $Id: algorithms.rb 258 2005-06-01 00:22:51Z ertai $
5
+
6
+ require 'module/autoload_tree'
7
+
8
+ module Algorithms
9
+
10
+ autoloaded_module(__FILE__)
11
+
12
+ end # module Algorithms
@@ -0,0 +1,142 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id$
5
+
6
+ require 'ruby_ex'
7
+ require 'choose'
8
+
9
+ module Algorithms
10
+
11
+ class SimulatedAnnealing
12
+
13
+ # Example:
14
+ #
15
+ # obj = MySimulatedAnnealingObject.new
16
+ #
17
+ # SimulatedAnnealing.new(
18
+ # :support => obj,
19
+ # :global_thresold => 200,
20
+ # :step_multiplicator => 0.63,
21
+ # :initial_temperature => 10,
22
+ # :initial_cost => obj.cost,
23
+ # :iteration_modulus => 500, # display status once a 500
24
+ # :step_modulus => 5000 # change the temperature once a 5000
25
+ # )
26
+ #
27
+ def initialize ( opts )
28
+ @support = opts[:support]
29
+ @global_thresold = opts[:global_thresold]
30
+ @step_multiplicator = opts[:step_multiplicator]
31
+ @temperature = opts[:initial_temperature]
32
+ @cur_cost = opts[:initial_cost]
33
+ @output = opts[:output] || STDOUT
34
+ @iteration_modulus = opts[:iteration_modulus] || 1
35
+ @step_modulus = opts[:step_modulus] || 1
36
+ @generator = opts[:generator]
37
+ @progression = ' '
38
+ @diff_cost = 0
39
+ @iteration = 0
40
+ @probability_threshold = 0
41
+ end
42
+
43
+
44
+ def run
45
+ @output.puts '---'
46
+ while @cur_cost > @global_thresold
47
+ # puts "Iteration(#{it}) #{@cur_cost}"
48
+ @transition = @support.choose_transition(@generator)
49
+ @diff_cost = @support.transition_cost(@cur_cost, @transition)
50
+
51
+ @iteration += 1
52
+ if (@iteration % @step_modulus).zero?
53
+ @temperature *= @step_multiplicator
54
+ end
55
+
56
+ if @diff_cost > 0
57
+ @probability_threshold = Math.exp(- @diff_cost.to_f / @temperature)
58
+ @proba = choose_probability(@generator)
59
+ if @proba < @probability_threshold
60
+ progression '+'
61
+ else
62
+ progression ' '
63
+ next
64
+ end
65
+ else
66
+ @probability_threshold = 1
67
+ @proba = 1
68
+ progression((@diff_cost.zero?)? '=' : '-')
69
+ end
70
+
71
+ @support.apply_transition(@transition)
72
+ @cur_cost += @diff_cost
73
+ end
74
+ end
75
+
76
+
77
+ def disp ( output=@output )
78
+ args = [ @progression, @diff_cost.abs, @temperature,
79
+ @proba, @probability_threshold, @cur_cost ]
80
+ fmt = '- { diff: %s%-7d, temp: %-4f, ' +
81
+ 'prob: %-7f, thres: %-7f, cost: %-5s }'
82
+ output.puts fmt % args
83
+ end
84
+
85
+
86
+ def summary
87
+ @output.puts %Q[
88
+ |---
89
+ |Found a mininum: #@cur_cost
90
+ |Temperature at the end: #@temperature
91
+ |Nb iterations: #@iteration
92
+ ].head_cut!
93
+ end
94
+
95
+
96
+ def progression ( x )
97
+ @progression = x
98
+ disp if (@iteration % @iteration_modulus).zero?
99
+ end
100
+
101
+
102
+ def choose_probability ( generator=nil )
103
+ 1.0.choose(generator)
104
+ end
105
+
106
+
107
+ module Support
108
+
109
+ def included ( aModule )
110
+
111
+ aModule.module_eval do
112
+
113
+ #
114
+ # Return a transition which can be applied by apply_transition.
115
+ #
116
+ def choose_transition ( generator=nil )
117
+ raise NotImplementedError
118
+ end
119
+
120
+ #
121
+ # Return a cost difference
122
+ #
123
+ def transition_cost ( current_cost, transition )
124
+ raise NotImplementedError
125
+ end
126
+
127
+ #
128
+ # Apply the given transition to your current object.
129
+ #
130
+ def apply_transition ( transition )
131
+ raise NotImplementedError
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ end # module Support
139
+
140
+ end # class SimulatedAnnealing
141
+
142
+ end # module Algorithms
data/src/ask.rb ADDED
@@ -0,0 +1,100 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2004, 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: ask.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+ require 'ruby_ex'
7
+
8
+ ANSWERS = [ :y, :n ]
9
+ ANSWER_NOT_VALID = 'Not a valid answer, please answer correctly'
10
+
11
+ # `ask', ask the user to answer, to your question.
12
+ #
13
+ # Example:
14
+ # ask('Commiting, are you sure', :n)
15
+ #
16
+ # produce => Commiting, are you sure (y/N):
17
+ # and wait your answer.
18
+ def ask ( aQuestion, theDefaultAnswer=:y, cin=STDIN, cout=STDOUT, cerr=STDERR )
19
+
20
+ yn = case theDefaultAnswer
21
+ when :y then ' [Y/n]: '
22
+ when :n then ' [y/N]: '
23
+ else raise ArgumentError, "not valid default answer #{theDefaultAnswer}"
24
+ end
25
+
26
+ loop do
27
+ cout.print aQuestion, yn
28
+ cout.flush
29
+
30
+ answer = cin.readline.chomp.downcase
31
+
32
+ return theDefaultAnswer if answer.empty?
33
+
34
+ answer = answer.to_sym
35
+
36
+ return answer if ANSWERS.include? answer
37
+
38
+ cerr.puts ANSWER_NOT_VALID
39
+ cout.puts
40
+ end
41
+
42
+ end
43
+
44
+ test_section __FILE__ do
45
+
46
+ class AskTest < Test::Unit::TestCase
47
+
48
+ def ask_checker ( question, default, answer, ref, out, err )
49
+ require 'stringio'
50
+ cin, cout, cerr = StringIO.new, StringIO.new, StringIO.new
51
+ cin.puts answer
52
+ cin.rewind
53
+ res = ask(question, default, cin, cout, cerr)
54
+ cout.rewind
55
+ cerr.rewind
56
+ assert_equal(res, ref, 'bad return value')
57
+ assert_equal(cout.readlines.join, out, 'bad standard output')
58
+ assert_equal(cerr.readlines.join, err, 'bad error output')
59
+ end
60
+
61
+ def test_bad_default
62
+ assert_raise(ArgumentError) { ask_checker('Q', :foo, '', :y, [], []) }
63
+ assert_nothing_raised do
64
+ ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
65
+ end
66
+ assert_nothing_raised do
67
+ ask_checker('Q', :n, 'y', :y, 'Q [y/N]: ', '')
68
+ end
69
+ end
70
+
71
+ def test_valid_default_yes
72
+ ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
73
+ ask_checker('Q', :y, 'Y', :y, 'Q [Y/n]: ', '')
74
+ ask_checker('Q', :y, 'n', :n, 'Q [Y/n]: ', '')
75
+ ask_checker('Q', :y, 'N', :n, 'Q [Y/n]: ', '')
76
+ ask_checker('Q', :y, '', :y, 'Q [Y/n]: ', '')
77
+ end
78
+
79
+ def test_valid_default_no
80
+ ask_checker('Q', :n, 'y', :y, 'Q [y/N]: ', '')
81
+ ask_checker('Q', :n, 'Y', :y, 'Q [y/N]: ', '')
82
+ ask_checker('Q', :n, 'n', :n, 'Q [y/N]: ', '')
83
+ ask_checker('Q', :n, 'N', :n, 'Q [y/N]: ', '')
84
+ ask_checker('Q', :n, '', :n, 'Q [y/N]: ', '')
85
+ end
86
+
87
+ def test_invalid_answer
88
+ ask_checker('Q', :n, "bad\ny", :y,
89
+ "Q [y/N]: \nQ [y/N]: ", ANSWER_NOT_VALID + "\n")
90
+ ask_checker('Q', :y, "ad\n\n", :y,
91
+ "Q [Y/n]: \nQ [Y/n]: ", ANSWER_NOT_VALID + "\n")
92
+ end
93
+
94
+ def test_ask1
95
+ ask_checker('Q', :y, 'y', :y, 'Q [Y/n]: ', '')
96
+ end
97
+
98
+ end # class AskTest
99
+
100
+ end
@@ -0,0 +1,303 @@
1
+ # Copyright:: Copyright (c) 2004, 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License:: Gnu General Public License.
4
+ # Revision:: $Id: attributed_class.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+
7
+ require 'ruby_ex'
8
+
9
+ # Extension to have inherited, class attributes,
10
+ # Where each class have his own attributes set.
11
+ module AttributedClass
12
+
13
+ class Attribute
14
+
15
+ attr_reader :name, :descr, :klass, :default_proc
16
+ attr_accessor :default
17
+
18
+ @@default_proc = nil
19
+
20
+ def initialize ( name, descr, *args, &block )
21
+ raise ArgumentError, 'need a symbol' unless name.is_a? Symbol
22
+ @name, @descr = name, descr
23
+ @default, @klass = nil, nil
24
+ @mandatory = false
25
+ @visible = true
26
+ if block_given?
27
+ @default_proc = block
28
+ else
29
+ @default_proc = @@default_proc
30
+ end
31
+ other = []
32
+
33
+ args.each do |arg|
34
+ case arg
35
+ when :mandatory then @mandatory = true
36
+ when :invisible then @visible = false
37
+ else other << arg
38
+ end
39
+ end
40
+
41
+ case other.size
42
+ when 1
43
+ if other[0].is_a? Class
44
+ @klass = [other[0]]
45
+ elsif other[0].is_a? Array and other[0].all? { |k| k.is_a? Class }
46
+ @klass = other[0]
47
+ else
48
+ @default = other[0]
49
+ end
50
+ when 2
51
+ a, b = other
52
+ if a.is_a? Class and b.is_a? a
53
+ @klass, @default = [a], b
54
+ elsif a.is_a? Array and
55
+ a.all? { |k| k.is_a? Class } and
56
+ a.any? { |k| b.is_a? k }
57
+ @klass, @default = a, b
58
+ elsif b.is_a? Class and a.is_a? b
59
+ @klass, @default = [b], a
60
+ elsif b.is_a? Array and
61
+ b.all? { |k| k.is_a? Class } and
62
+ b.any? { |k| a.is_a? k }
63
+ @klass, @default = b, a
64
+ else
65
+ raise ArgumentError, 'need a Class and a default value'
66
+ end
67
+ when 0
68
+ else raise ArgumentError, 'too many values'
69
+ end
70
+
71
+ # removed because it causes some probleme when distributed such a class
72
+ #[@name, @descr, @default].each { |x| x.freeze }
73
+ end
74
+
75
+ def visible?
76
+ @visible
77
+ end
78
+
79
+ def invisible?
80
+ !@visible
81
+ end
82
+
83
+ def mandatory?
84
+ @mandatory
85
+ end
86
+
87
+ def valid? ( anObject )
88
+ return false if mandatory? and anObject.nil?
89
+ @klass.nil? or @klass.empty? or @klass.any? { |k| anObject.is_a? k }
90
+ end
91
+
92
+
93
+ def self.set_default_proc ( &block )
94
+ @@default_proc = block
95
+ end
96
+
97
+
98
+ def help ( output=STDERR )
99
+ output << "#@name: #@descr"
100
+ output << " [#@klass]" unless @klass.nil?
101
+ other = []
102
+ other << 'mandatory' if mandatory?
103
+ other << 'invisible' if invisible?
104
+ output << " (#{other.join(', ')})" unless other.empty?
105
+ output
106
+ end
107
+
108
+ def inspect
109
+ help('#<') + '>'
110
+ end
111
+
112
+ end # class Attribute
113
+
114
+
115
+ class MethodAttribute < Attribute
116
+ VISIBILITIES = [ :private, :protected, :public, :unknown ]
117
+ attr_reader :visibility
118
+ def private?
119
+ @visibility == :private
120
+ end
121
+ def protected?
122
+ @visibility == :protected
123
+ end
124
+ def public?
125
+ @visibility == :public
126
+ end
127
+ def initialize ( name, descr, *args )
128
+ vis = args.find { |arg| VISIBILITIES.include? arg }
129
+ args.delete(vis) unless vis.nil?
130
+ @visibility = vis || :unknown
131
+ super(name, descr, *args)
132
+ end
133
+ end # class MethodAttribute
134
+
135
+
136
+ class AttributeError < Exception
137
+
138
+ def initialize ( anObject, anAttr, val=nil )
139
+ if val.nil?
140
+ super "Missing the #{anAttr.name} attribute for `#{anObject}'"
141
+ else
142
+ super "Attribute #{anAttr.name} wait for a `#{anAttr.klass}' not " +
143
+ "`#{val}' in #{anObject}"
144
+ end
145
+ end
146
+
147
+ end # class AttributeError
148
+
149
+
150
+ def self.included ( aClass )
151
+
152
+ aClass.module_eval do
153
+
154
+ def initialize_attributes ( *default_proc_args )
155
+ self.class.attributes.each do |attr|
156
+ next if attr.is_a? MethodAttribute
157
+ if attr.default_proc.nil? and attr.default.nil?
158
+ instance_variable_set("@#{attr.name}", nil)
159
+ else
160
+ unless attr.default_proc.nil?
161
+ pr = attr.default_proc
162
+ if pr.arity == default_proc_args.size
163
+ default = pr[*default_proc_args]
164
+ else
165
+ default = pr[attr, *default_proc_args]
166
+ end
167
+ attr.default = default
168
+ end
169
+ send "#{attr.name}=", attr.default
170
+ end
171
+ end
172
+ end
173
+
174
+ def self.set_default_proc ( &block )
175
+ Attribute.set_default_proc(&block)
176
+ end
177
+
178
+ def each_attribute ( &block )
179
+ self.class.attributes.each do |attr|
180
+ block[attr, send(attr.name)]
181
+ end
182
+ end
183
+
184
+ def each_variable_attribute ( &block )
185
+ self.class.attributes.each do |attr|
186
+ next if attr.is_a? MethodAttribute
187
+ block[attr, send(attr.name)]
188
+ end
189
+ end
190
+
191
+ def each_method_attribute ( &block )
192
+ self.class.attributes.each do |attr|
193
+ next unless attr.is_a? MethodAttribute
194
+ block[attr, send(attr.name)]
195
+ end
196
+ end
197
+
198
+ def check_attribute ( attr, anObject )
199
+ unless attr.valid? anObject
200
+ raise AttributeError.new(self, attr, anObject)
201
+ end
202
+ end
203
+
204
+ def check_attributes
205
+ each_variable_attribute(&method(:check_attribute))
206
+ end
207
+
208
+ def self.help ( output=STDERR )
209
+ output.puts "#{self.to_s}:"
210
+ attributes.each do |attr|
211
+ output << ' '
212
+ attr.help(output)
213
+ output.puts '.'
214
+ end
215
+ end
216
+
217
+ def self.attributes
218
+ unless defined? @attributes
219
+ if superclass and superclass.respond_to? :attributes
220
+ @attributes = superclass.attributes.dup
221
+ else
222
+ @attributes = []
223
+ end
224
+ end
225
+ @attributes
226
+ end
227
+
228
+ def self.attribute ( name, descr, *args, &block )
229
+ attr = Attribute.new(name, descr, *args, &block)
230
+ unless method_defined? name
231
+ if att = get_attribute(name, MethodAttribute)
232
+ attributes.delete(att)
233
+ end
234
+ attr_reader name
235
+ end
236
+ meth = "#{name}="
237
+ unless method_defined? meth
238
+ if att = get_attribute(meth, MethodAttribute)
239
+ attributes.delete(att)
240
+ end
241
+ attr_writer name
242
+ end
243
+ attributes << attr
244
+ end
245
+
246
+ def self.method_attribute ( name, descr, *args, &block )
247
+ # unless method_defined? name
248
+ # raise ArgumentError, "Undefined method #{name}"
249
+ # end
250
+ attr = MethodAttribute.new(name, descr, *args, &block)
251
+ attributes << attr
252
+ end
253
+
254
+ def self.get_attribute ( name, type=nil )
255
+ if type.nil?
256
+ attributes.find { |attr| attr.name == name }
257
+ else
258
+ attributes.find { |attr| attr.name == name && attr.is_a?(type) }
259
+ end
260
+ end
261
+
262
+ end
263
+
264
+ end
265
+
266
+ end # module AttributedClass
267
+
268
+
269
+
270
+ test_section __FILE__ do
271
+
272
+ class AttributedClassTest < Test::Unit::TestCase
273
+
274
+ def test_simple
275
+ c = Class.new
276
+ assert_nothing_raised do
277
+ c.module_eval { include AttributedClass }
278
+ end
279
+ assert_equal([], c.attributes)
280
+ assert_nothing_raised do
281
+ c.module_eval { attribute :foo, 'foofoo', 42 }
282
+ end
283
+ cc, d, dd = nil, nil, nil
284
+ assert_nothing_raised { cc = c.new }
285
+ assert_equal(nil, cc.foo)
286
+ assert_nothing_raised { cc.initialize_attributes }
287
+ assert_equal(42, cc.foo)
288
+ assert_nothing_raised { cc.foo = 32 }
289
+ assert_equal(32, cc.foo)
290
+ assert_nothing_raised { d = Class.new(c) }
291
+ assert_nothing_raised do
292
+ d.module_eval { attribute :bar, 'foofoo', :mandatory }
293
+ end
294
+ assert_nothing_raised { dd = d.new }
295
+ assert_nothing_raised { dd.initialize_attributes }
296
+ assert_raise(AttributedClass::AttributeError) do
297
+ dd.check_attributes
298
+ end
299
+ end
300
+
301
+ end # class AttributedClassTest
302
+
303
+ end