ruby_ex 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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