ctioga 1.11.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 (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