ctioga 1.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/COPYING +340 -0
  2. data/ctioga/bin/ctable +28 -0
  3. data/ctioga/bin/ctioga +37 -0
  4. data/ctioga/doc/ctable.1 +156 -0
  5. data/ctioga/doc/ctioga.1 +2363 -0
  6. data/ctioga/examples/README +46 -0
  7. data/ctioga/examples/ctioga.gnuplot +4 -0
  8. data/ctioga/examples/ctioga_within_tioga.rb +53 -0
  9. data/ctioga/examples/ctiogarc.rb +24 -0
  10. data/ctioga/examples/include_1.rb +15 -0
  11. data/ctioga/examples/noise.dat +100 -0
  12. data/ctioga/examples/noise.rb +13 -0
  13. data/ctioga/examples/trig.csv +100 -0
  14. data/ctioga/examples/trig.dat +100 -0
  15. data/ctioga/examples/trig.rb +14 -0
  16. data/ctioga/examples/trigh.dat +100 -0
  17. data/ctioga/examples/trigh.rb +10 -0
  18. data/ctioga/examples/tutorial +763 -0
  19. data/ctioga/examples/tutorial.sh +269 -0
  20. data/ctioga/tests/README +14 -0
  21. data/ctioga/tests/axes.sh +40 -0
  22. data/ctioga/tests/basic.sh +11 -0
  23. data/ctioga/tests/draw.sh +24 -0
  24. data/ctioga/tests/histograms.sh +14 -0
  25. data/ctioga/tests/insets.sh +41 -0
  26. data/ctioga/tests/layouts.sh +29 -0
  27. data/ctioga/tests/legends.sh +113 -0
  28. data/ctioga/tests/styles.sh +43 -0
  29. data/ctioga/tests/test_style.sh +8 -0
  30. data/ctioga/tests/tests.sh +24 -0
  31. data/ctioga/tests/text_backend.sh +83 -0
  32. data/ctioga/tests/tioga_defaults.rb +18 -0
  33. data/lib/CTioga/axes.rb +904 -0
  34. data/lib/CTioga/backends.rb +88 -0
  35. data/lib/CTioga/boundaries.rb +224 -0
  36. data/lib/CTioga/ctable.rb +134 -0
  37. data/lib/CTioga/curve_style.rb +246 -0
  38. data/lib/CTioga/debug.rb +199 -0
  39. data/lib/CTioga/dimension.rb +133 -0
  40. data/lib/CTioga/elements.rb +17 -0
  41. data/lib/CTioga/elements/base.rb +84 -0
  42. data/lib/CTioga/elements/containers.rb +578 -0
  43. data/lib/CTioga/elements/curves.rb +368 -0
  44. data/lib/CTioga/elements/tioga_primitives.rb +440 -0
  45. data/lib/CTioga/layout.rb +595 -0
  46. data/lib/CTioga/legends.rb +29 -0
  47. data/lib/CTioga/legends/cmdline.rb +187 -0
  48. data/lib/CTioga/legends/item.rb +164 -0
  49. data/lib/CTioga/legends/style.rb +257 -0
  50. data/lib/CTioga/log.rb +73 -0
  51. data/lib/CTioga/movingarrays.rb +131 -0
  52. data/lib/CTioga/partition.rb +271 -0
  53. data/lib/CTioga/plot_style.rb +230 -0
  54. data/lib/CTioga/plotmaker.rb +1677 -0
  55. data/lib/CTioga/shortcuts.rb +69 -0
  56. data/lib/CTioga/structures.rb +82 -0
  57. data/lib/CTioga/styles.rb +140 -0
  58. data/lib/CTioga/themes.rb +581 -0
  59. data/lib/CTioga/themes/classical.rb +82 -0
  60. data/lib/CTioga/themes/demo.rb +63 -0
  61. data/lib/CTioga/themes/fits.rb +91 -0
  62. data/lib/CTioga/themes/mono.rb +33 -0
  63. data/lib/CTioga/tioga.rb +32 -0
  64. data/lib/CTioga/utils.rb +173 -0
  65. data/lib/MetaBuilder/Parameters/dates.rb +38 -0
  66. data/lib/MetaBuilder/Parameters/lists.rb +132 -0
  67. data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
  68. data/lib/MetaBuilder/Parameters/strings.rb +86 -0
  69. data/lib/MetaBuilder/Parameters/styles.rb +75 -0
  70. data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
  71. data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
  72. data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
  73. data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
  74. data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
  75. data/lib/MetaBuilder/descriptions.rb +603 -0
  76. data/lib/MetaBuilder/factory.rb +101 -0
  77. data/lib/MetaBuilder/group.rb +57 -0
  78. data/lib/MetaBuilder/metabuilder.rb +10 -0
  79. data/lib/MetaBuilder/parameter.rb +374 -0
  80. data/lib/MetaBuilder/parameters.rb +11 -0
  81. data/lib/MetaBuilder/qt4.rb +8 -0
  82. data/lib/SciYAG/Backends/backend.rb +379 -0
  83. data/lib/SciYAG/Backends/binner.rb +168 -0
  84. data/lib/SciYAG/Backends/cache.rb +102 -0
  85. data/lib/SciYAG/Backends/dataset.rb +158 -0
  86. data/lib/SciYAG/Backends/descriptions.rb +469 -0
  87. data/lib/SciYAG/Backends/filters.rb +25 -0
  88. data/lib/SciYAG/Backends/filters/average.rb +134 -0
  89. data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
  90. data/lib/SciYAG/Backends/filters/filter.rb +70 -0
  91. data/lib/SciYAG/Backends/filters/norm.rb +39 -0
  92. data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
  93. data/lib/SciYAG/Backends/filters/sort.rb +43 -0
  94. data/lib/SciYAG/Backends/filters/strip.rb +34 -0
  95. data/lib/SciYAG/Backends/filters/trim.rb +64 -0
  96. data/lib/SciYAG/Backends/gnuplot.rb +131 -0
  97. data/lib/SciYAG/Backends/math.rb +108 -0
  98. data/lib/SciYAG/Backends/mdb.rb +462 -0
  99. data/lib/SciYAG/Backends/multitext.rb +96 -0
  100. data/lib/SciYAG/Backends/source.rb +64 -0
  101. data/lib/SciYAG/Backends/text.rb +339 -0
  102. data/lib/SciYAG/backends.rb +16 -0
  103. metadata +191 -0
@@ -0,0 +1,102 @@
1
+ # cache.rb : A cache for various objects
2
+ # Copyright (C) 2007 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module SciYAG
19
+
20
+ module Backends
21
+
22
+ # The Cache class aims at providing a small and easy-to-use cache
23
+ # with validation checking, usage statistics and flushing (automatically
24
+ # removes some entries when it grows too big).
25
+ class Cache
26
+
27
+ # An element of the cache
28
+ class CacheEntry
29
+ # The name under which it is cached
30
+ attr_accessor :name
31
+
32
+ # The data cached
33
+ attr_accessor :data
34
+
35
+ # The meta-data that should be matched against when checking
36
+ # the cache's relevance
37
+ attr_accessor :meta_data
38
+
39
+ def initialize(name, data, meta_data)
40
+ @name = name
41
+ @data = data
42
+ # Maybe we should even .dup the contents ?
43
+ @meta_data = meta_data.dup # (just to make sure)
44
+ end
45
+
46
+ # Checks that the meta_data corresponds to this cache entry
47
+ def relevant?(meta_data)
48
+ return @meta_data == meta_data
49
+ end
50
+ end
51
+
52
+ # Records all accessed items and the number of cache misses/succes
53
+ # and so on.
54
+ attr_accessor :statistics
55
+
56
+ # Creates a Cache
57
+ def initialize
58
+ # The cache itself
59
+ @cache = {}
60
+
61
+ @statistics = {}
62
+ end
63
+
64
+ # Look inside the cache for a cached element. If it is found and
65
+ # up-to-date, it is returned. If not, #get_cache runs _code_,
66
+ # stores its return value as a new cache for (_name_, _meta_data_)
67
+ # and returns it.
68
+ def get_cache(name, meta_data, &code)
69
+ if (@cache.key?(name)) and
70
+ ((cached = @cache[name]).relevant?(meta_data))
71
+ stats(name)[:accesses] += 1
72
+ return cached.data
73
+ elsif @cache.key?(name)
74
+ stats(name)[:updates] += 1
75
+ end
76
+ stats(name)[:misses] += 1
77
+
78
+ # Now, we run the code to update the cache entry:
79
+ data = code.call
80
+ @cache[name] = CacheEntry.new(name, data, meta_data)
81
+ return data
82
+ end
83
+
84
+ # Returns the statistics for the given item, or create them
85
+ # on the fly if necessary:
86
+ def stats(name)
87
+ if @statistics[name]
88
+ return @statistics[name]
89
+ else
90
+ @statistics[name] = {
91
+ :accesses => 0,
92
+ :misses => 0,
93
+ :updates => 0
94
+ }
95
+ return @statistics[name]
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+ end
@@ -0,0 +1,158 @@
1
+ # dataset.rb : The implementation of data sets
2
+ # Copyright (C) 2006 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ require 'MetaBuilder/metabuilder'
19
+
20
+ require 'forwardable'
21
+
22
+ module SciYAG
23
+
24
+ module Backends
25
+
26
+
27
+ # An abstract class representing a DataSet. You should consider using
28
+ # a subclass, DataSet2D or DataSet3D. A DataSet must be either 2D or 3D,
29
+ # and redefine #is_2D? and #is_3D? accordingly.
30
+ #
31
+ # As DataSets grow more complex, it is likely that it will be less and
32
+ # less easy to tweak directly the data inside. So, please, do use the
33
+ # accessors and tweakers provided in the interface, else you'll expose
34
+ # yourself to some intensive breakages when I get more clever.
35
+ # Meanwhile, the apply(meth,where,*rest) method should be used to keep
36
+ # everything in sync when you apply a method to a Dvector.
37
+ class DataSet
38
+
39
+ extend Forwardable
40
+
41
+ # The #creation_context attribute is a hash containing a :backend key
42
+ # indicating the name of the Backend created and filled with the
43
+ # contents of the backend's DescriptionInclude#save_state function.
44
+ attr_accessor :creation_context
45
+
46
+ # The data of the set.
47
+ attr_accessor :data
48
+
49
+ # Errors on data, if applicable.
50
+ # For 2D data, this will just be a hash with the following Dvectors:
51
+ # :xmin, :xmax for errors on the x values + :x, to make sure
52
+ # we keep it up-to-date.
53
+ # :ymin, :ymax for errors on y + :y.
54
+ #
55
+ # Both ?min and ?max have to be specified if any output should be
56
+ # created.
57
+ attr_accessor :errors
58
+
59
+ # The metadata given when the set was created.
60
+ attr_accessor :meta_data
61
+
62
+ def is_2D?
63
+ return false
64
+ end
65
+
66
+ def is_3D?
67
+ return false
68
+ end
69
+
70
+ # A few forwarded stuff to make the DataSet behave exactly like
71
+ # Function and it's future 3D counterpart
72
+
73
+ def_delegators :@data, :x, :y, :z
74
+
75
+ def initialize(context, data, errors = {}, meta_data = {})
76
+ if context.respond_to? :save_state
77
+ @creation_context = context.save_state
78
+ @creation_context[:backend] = context.description.name
79
+ else
80
+ @creation_context = context.dup
81
+ end
82
+ @data = data
83
+ @errors = errors
84
+ @meta_data = meta_data
85
+ end
86
+
87
+ # Apply a Dvector operation to the given dimension, including
88
+ # everything that could be left somewhere in the error bars.
89
+ # It should be very powerful in the end.
90
+ #
91
+ # Beautiful, isn't it ??
92
+ def apply(what, where, *rest)
93
+ self.send(where).send(what, *rest)
94
+ for key,values in @errors
95
+ if key.to_s =~ /^#{where}/ # Very much overkill, but, well...
96
+ values.send(what,*rest)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Does a redirection to the underlying @data if that makes sense.
102
+ def method_missing(sym, *args, &b)
103
+ if @data.respond_to?(sym)
104
+ @data.send(sym, *args, &b)
105
+ elsif x.respond_to?(sym) # We are trying to apply something
106
+ # like data.mul!(:x, factor)
107
+ apply(sym,args.shift,*args,&b)
108
+ else
109
+ super
110
+ end
111
+ end
112
+
113
+ # Returns all the Dvectors held by this dataset. Can be used
114
+ # to Dvector#replace them
115
+ def all_vectors
116
+ list = [@data.x, @data.y, *@errors.values]
117
+ list << @data.z if @data.respond_to?(:z)
118
+ return list
119
+ end
120
+
121
+ # Sorts the data according to the X value. This also includes
122
+ # the error bars.
123
+ def sort!
124
+ new_error = {}
125
+ idx_vector = Dobjects::Dvector.new(@data.x.size) do |i|
126
+ i
127
+ end
128
+ f = Dobjects::Function.new(@data.x.dup, idx_vector)
129
+ f.sort
130
+ for vector in all_vectors
131
+ new_vector = Dobjects::Dvector.new(vector.size) do |i|
132
+ vector[f.y[i]]
133
+ end
134
+ vector.replace(new_vector)
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ # A 2-dimensionnal DataSet
141
+ class DataSet2D < DataSet
142
+
143
+ def is_2D?
144
+ return true
145
+ end
146
+
147
+ end
148
+
149
+ # A 3-dimensionnal DataSet
150
+ class DataSet3D < DataSet
151
+ def is_3D?
152
+ return true
153
+ end
154
+ end
155
+
156
+
157
+ end
158
+ end
@@ -0,0 +1,469 @@
1
+ # description.rb : How do classes describe themselves
2
+ # Copyright (C) 2006 Vincent Fourmond
3
+
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ module SciYAG
19
+
20
+ module Backends
21
+
22
+
23
+ module Descriptions
24
+
25
+ # A module to convert text input into an object of the given type.
26
+ module Conversion
27
+
28
+ # The regular expression that matches 'true' for boolean values:
29
+ TRUE_RE = /^\s*(true|yes)\s*$/i
30
+
31
+ # The regular expression that matches 'false'
32
+ FALSE_RE = /^\s*(false|no(ne)?)\s*$/i
33
+
34
+ # Converts +text+ to the given type. It support different schemes:
35
+ # * if +type+ is String, Float, Array of Integer, the values are
36
+ # converted using the appropriate functions;
37
+ # * if +type+ is a string, the type is considered to be String;
38
+ # this leaves a possibility to implement a more precise
39
+ # mechanism for choosing; see Descriptions::Parameter#type
40
+ # * if +type+ is :bool, the text is interpreted as a true/false
41
+ # expression.
42
+ # * if +type+ is an array (of symbols/strings), then a valid input
43
+ # is one of the elements of the arrays (it is converted to symbols
44
+ # if necessary)
45
+ # * if +type+ is a hash, it behaves as if the keys had been
46
+ # specified as an array. The values can be used to provide
47
+ # a proper name;
48
+ # * for any other input, +type+ is assumed to be a class, and its
49
+ # constructor should support build from 1 argument, a String.
50
+
51
+ def text_to_type(text, type)
52
+ # warning: case cannot be used, as it is the comparison
53
+ # === which is used, that is is_a?.
54
+ if type == String
55
+ value = String(text)
56
+ elsif type.is_a?(String) # some special specification;
57
+ # always a string
58
+ value = String(text)
59
+ elsif type == Float
60
+ value = Float(text)
61
+ elsif type == Array
62
+ value = Array(text)
63
+ elsif type == Integer
64
+ value = Integer(text)
65
+ elsif type.is_a?(Array)
66
+ h = {}
67
+ type.each do |a|
68
+ h[a.to_s] = a
69
+ end
70
+ return h[text] # No checking done...
71
+ elsif type.is_a?(Hash)
72
+ return text_to_type(text, type.keys)
73
+ elsif type == :bool
74
+ if text =~ TRUE_RE
75
+ return true
76
+ else
77
+ return false
78
+ end
79
+ else
80
+ value = type.new(text)
81
+ end
82
+ return value
83
+ end
84
+
85
+ end
86
+
87
+
88
+ # A class that describes one parameter that will be fixed at
89
+ # run-time by the user.
90
+ class Parameter
91
+ include Conversion
92
+ # The short name of the parameter
93
+ attr_accessor :name
94
+
95
+ # The long name of the parameter, to be translated
96
+ attr_accessor :long_name
97
+
98
+ # The function names that should be used to set the symbol and
99
+ # retrieve it's current value. The corresponding functions should
100
+ # read or return a string, and writer(reader) should be a noop.
101
+ attr_accessor :reader_symbol, :writer_symbol
102
+
103
+ # The description
104
+ attr_accessor :description
105
+
106
+ # The type of the parameter. It can take several values;
107
+ # see Descriptions::Conversion.text_to_type. Moreover, several values
108
+ # for a string parameter can be interpreted by systems in charge
109
+ # of querying the parameter:
110
+ #
111
+ # * <tt>"File: ..."</tt> represents a file, and the rest is the
112
+ # filter, Qt style;
113
+ # * <tt>"Set"</tt> represents a data set, which can queried for by
114
+ # the sets_available function.
115
+ attr_accessor :type
116
+
117
+ def initialize(name, writer_symbol,
118
+ reader_symbol,
119
+ long_name,
120
+ type,
121
+ description)
122
+ @name = name
123
+ @writer_symbol = writer_symbol
124
+ @reader_symbol = reader_symbol
125
+ @description = description
126
+ @long_name = long_name
127
+ @type = type
128
+ end
129
+
130
+ # Sets the value of the given parameter in the _target_. It tries
131
+ # to be clever somehow, using @type to know what should be
132
+ # expected. See the text_to_type function.
133
+
134
+ def set(target, value)
135
+ value = text_to_type(value, @type)
136
+ target.send(@writer_symbol, value)
137
+ end
138
+
139
+ # Aquires the value from the backend, and returns it
140
+ def get(target)
141
+ target.send(@reader_symbol).to_s
142
+ end
143
+
144
+ def value(v)
145
+ return text_to_type(v, @type)
146
+ end
147
+ end
148
+
149
+ # The base class for all descriptions. A description describes
150
+ # a "plugin" class. It has the following attributes:
151
+ #
152
+ # * the basic name, code-like, which is used mainly for internal
153
+ # purposes;
154
+ # * the long name, which has to be translated
155
+ # * the description itself, some small text describing the nature
156
+ # of the plugin
157
+ # * a list of parameters the plugin can take, along with their
158
+ # description. These are Parameter .
159
+ #
160
+ class Description
161
+ # the class to instantiate.
162
+ attr_accessor :class
163
+
164
+ # The name of the backend (short, code-like)
165
+ attr_accessor :name
166
+
167
+ # (text) description !
168
+ attr_accessor :description
169
+
170
+ # Long name, the one for public display
171
+ attr_accessor :long_name
172
+
173
+ # The hash holding parameters. Useful mainly for subclasses
174
+ attr_reader :param_hash
175
+
176
+ # The parameter list
177
+ attr_reader :param_list
178
+
179
+ # The list of parameters that have to be fed into the initialize
180
+ # function.
181
+ attr_accessor :init_param_list
182
+
183
+
184
+ # Initializes a Description
185
+ def initialize(cls, name, long_name, description = "")
186
+ @class = cls
187
+ @name = name
188
+ @long_name = long_name
189
+ @description = description
190
+ @param_list = []
191
+ @param_hash = {}
192
+ @init_param_list = []
193
+ end
194
+
195
+ # Creates an instance of the class, forwards parameters to the
196
+ # initialize method.
197
+ def instantiate(*args)
198
+ return @class.new(*args)
199
+ end
200
+
201
+ # Prepares the argument list for instantiate based on
202
+ # init_param_list and the text arguments given:
203
+ def prepare_instantiate_arglist(*args)
204
+ target = []
205
+ for pars in @init_param_list
206
+ target << pars.value(args.shift)
207
+ end
208
+ return target
209
+ end
210
+
211
+ def add_param(param)
212
+ @param_list << param
213
+
214
+ # Three different cross-linkings.
215
+ @param_hash[param.reader_symbol] = param
216
+ @param_hash[param.writer_symbol] = param
217
+ @param_hash[param.name] = param
218
+ end
219
+
220
+ def param_set(i, p)
221
+ param = p
222
+ instance = i
223
+ return proc { |x| param.set(instance,x) }
224
+ end
225
+
226
+ # Pushes the names of the params onto @init_list_param_list.
227
+ # Arguments have to be strings.
228
+ def init_params(*args)
229
+ @init_param_list += args
230
+ end
231
+
232
+ # Fills an OptionParser with all the parameters the
233
+ # Backend should be able to take.
234
+
235
+ def fill_parser(instance, parser, uniquify = true)
236
+ parser_banner(instance, parser)
237
+ parser_options(instance, parser, uniquify)
238
+ end
239
+
240
+ # Fills a parser with options (and only that)
241
+ def parser_options(instance, parser, uniquify = true)
242
+ raise "The instance is not of the right class" unless
243
+ instance.is_a? @class
244
+ for param in @param_list
245
+ if uniquify
246
+ param_name = "--#{@name}-#{param.name} #{param.name.upcase}"
247
+ else
248
+ param_name = "--#{param.name} #{param.name.upcase}"
249
+ end
250
+ parser.on(param_name, param.description,
251
+ param_set(instance, param))
252
+ end
253
+ end
254
+
255
+ # The parsers's banner.
256
+ def parser_banner(instance, parser)
257
+ # nothing by default
258
+ end
259
+
260
+ # Creates a parser entry in +parser+ that creates a new instance of the
261
+ # description's class and feed it to the +result+ method of
262
+ # the +receiver+. You can specify an optionnal +prefix+ for the
263
+ # option's name.
264
+ def parser_instantiate_option(parser, receiver, result, prefix = "")
265
+ op_name = "--#{prefix}#{@name}"
266
+ for arg in @init_param_list
267
+ op_name += " " + arg.name.upcase
268
+ end
269
+ # cool !
270
+ parser.on(op_name, @description) do |*a|
271
+ b = prepare_instantiate_arglist(*a)
272
+ instance = instantiate(*b)
273
+ receiver.send(result, instance)
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ # This module should be used with +include+ to provide the class with
280
+ # descriptions functionnalities. You also need to +extend+
281
+ # DescriptionExtend
282
+ module DescriptionInclude
283
+ # Returns the description associated with the backend object. Actually
284
+ # returns the one associated with the class.
285
+ def description
286
+ return self.class.description
287
+ end
288
+
289
+ # Fills an OptionParser with their parameters. Most probably, the
290
+ # default implementation should do for most cases. _uniquify_ asks
291
+ # if we should try to make the command-line options as unique as
292
+ # reasonable to do ?
293
+ def fill_parser(parser, uniquify = true)
294
+ description.fill_parser(self, parser, uniquify)
295
+ end
296
+
297
+ def parser_banner(parser)
298
+ description.parser_banner(self,parser)
299
+ end
300
+
301
+ def parser_options(parser, uniquify = true)
302
+ description.parser_options(self,parser, uniquify)
303
+ end
304
+
305
+ # Returns the long name of the backend:
306
+ def long_name
307
+ return description.long_name
308
+ end
309
+ end
310
+
311
+ # This module should be used with +extend+ to provide the class with
312
+ # descriptions functionnalities. You also need to +include+
313
+ # DescriptionInclude. Please not that all the *instance* methods
314
+ # defined here will become *class* methods there.
315
+ module DescriptionExtend
316
+
317
+ # Returns the description of the class.
318
+ def description
319
+ return @description
320
+ end
321
+
322
+ # Sets the description of the class. Classes should provide
323
+ # an easier interface for that.
324
+ def set_description(desc)
325
+ @description = desc
326
+ end
327
+
328
+ # Like param, but doesn't define an accessor.
329
+ def param_noaccess(writer, reader, name, long_name, type = String,
330
+ desc = "")
331
+ raise "Use describe first" if description.nil?
332
+ param = Descriptions::Parameter.new(name, writer, reader,
333
+ long_name,
334
+ type, desc)
335
+ description.add_param(param)
336
+ return param
337
+ end
338
+
339
+ # Tells if the current object is a base ancestor ?
340
+ def base_ancestor?
341
+ if superclass.respond_to?(:lookup_description_extend_ancestor)
342
+ return false
343
+ else
344
+ return true
345
+ end
346
+ end
347
+
348
+ # Retrieves the most ancient ancestor that has included
349
+ # DescriptionExtend.
350
+ def lookup_description_extend_ancestor
351
+ t = self
352
+ while not t.base_ancestor?
353
+ t = t.superclass
354
+ end
355
+ return t
356
+ end
357
+
358
+ # Valid only in the base class
359
+ def description_list_base
360
+ if base_ancestor?
361
+ return @description_list
362
+ else
363
+ raise "This function should only be called from the " +
364
+ "base ancestor"
365
+ end
366
+ end
367
+
368
+ # Valid only in the base class
369
+ def set_description_list_base()
370
+ @description_list = []
371
+ end
372
+
373
+
374
+ # Returns the list of descriptions associated with the base
375
+ # class
376
+ def description_list
377
+ return lookup_description_extend_ancestor.description_list_base
378
+ end
379
+
380
+ # Valid only in the base class
381
+ def description_hash_base
382
+ if base_ancestor?
383
+ return @description_hash
384
+ else
385
+ raise "This function should only be called from the " +
386
+ "base ancestor"
387
+ end
388
+ end
389
+
390
+ # Valid only in the base class
391
+ def set_description_hash_base()
392
+ @description_hash = {}
393
+ end
394
+
395
+ # Returns the hash of descriptions associated with the base
396
+ # class
397
+ def description_hash
398
+ return lookup_description_extend_ancestor.description_hash_base
399
+ end
400
+
401
+
402
+ # Registers a description in the base ancestor.
403
+ def register_description(desc)
404
+ description_list << desc
405
+ description_hash[desc.name] = desc
406
+ end
407
+
408
+ # Redefined so that we can add the description lists only in the
409
+ # base ancestor.
410
+ def DescriptionExtend.extend_object(t)
411
+ # First call super, to make sure the methods are here !
412
+ super
413
+ if t.base_ancestor?
414
+ t.set_description_list_base
415
+ t.set_description_hash_base
416
+ end
417
+ end
418
+
419
+
420
+ # This shortcut creates an accessor for the given symbol and registers
421
+ # it at the same time. Use if *after* describe. Something like
422
+ #
423
+ # param :size, "size", "Size", Integer , "The size of the backend"
424
+ #
425
+ # should be perfectly fine.
426
+ def param(symbol, name, long_name, type = String,
427
+ desc = "")
428
+ # We use the accessor = symbol.
429
+ param_noaccess((symbol.to_s + "=").to_sym,
430
+ symbol, name, long_name,
431
+ type, desc)
432
+ # Creates an accessor
433
+ attr_accessor symbol
434
+ end
435
+
436
+ # The parameters the class should inherit from its direct parents
437
+ # (which could have in turn inherited them...).
438
+ def inherit(*names)
439
+ if self.superclass.respond_to?(:description)
440
+ parents_params = self.superclass.description.param_hash
441
+ for n in names
442
+ if parents_params.key?(n)
443
+ description.add_param(parents_params[n])
444
+ else
445
+ warn "Param #{n} not found"
446
+ end
447
+ end
448
+ else
449
+ warn "The parent class has no description"
450
+ end
451
+ end
452
+
453
+ # Adds the params to the list of init params. Better use init_param
454
+ def init_params(*params)
455
+ description.init_params(*params)
456
+ end
457
+
458
+ # Creates a parameter which has to be used for instancitation
459
+ def init_param(name, long_name, type, desc)
460
+ p = param_noaccess(nil, nil, name, long_name, type, desc)
461
+ init_params(p)
462
+ end
463
+
464
+ end
465
+
466
+ end
467
+ end
468
+
469
+ end