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,65 @@
1
+ # numbers.rb : Qt methods for numbers
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
+
19
+ module MetaBuilder
20
+
21
+ # The module ParameterTypes should be used for all subclasses of
22
+ # ParameterType, to keep the place clean and tidy.
23
+ module ParameterTypes
24
+
25
+ # An integer
26
+ class IntegerParameter
27
+
28
+ def qt4_create_input_widget(parent = nil, target = nil, style = nil)
29
+ return Qt4MB::IntegerInputWidget.new(parent, self)
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ module Qt4MB
36
+
37
+ # A spinbox to choose Integers.
38
+ class IntegerInputWidget < Qt::SpinBox
39
+
40
+ # Emitted when the value is changed. Connected to
41
+ # editingFinished().
42
+ signals 'value_changed()'
43
+
44
+ # Creates a Generic widget. +type+ is the ParameterType child
45
+ # instance for whom we should do conversion.
46
+ def initialize(parent, type)
47
+ super(parent)
48
+ @type = type
49
+ connect(self, SIGNAL('valueChanged(int)'),
50
+ SIGNAL('value_changed()'))
51
+ set_range(-(2**30), 2**30)
52
+ end
53
+
54
+ def text
55
+ return @type.type_to_string(value)
56
+ end
57
+
58
+ def text=(t)
59
+ self.value=(@type.string_to_type(t))
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,106 @@
1
+ # numbers.rb : Qt methods for strings
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
+
19
+ module MetaBuilder
20
+
21
+ # The module ParameterTypes should be used for all subclasses of
22
+ # ParameterType, to keep the place clean and tidy.
23
+ module ParameterTypes
24
+
25
+ # An integer
26
+ class FileParameter
27
+
28
+ def qt4_create_input_widget(parent = nil, target = nil, style = nil)
29
+ return Qt4MB::FileInputWidget.new(parent, self)
30
+ end
31
+
32
+ # Directly wraps to Qt::FileDialog.get_open_file_name;
33
+ # the _title_ is therefore ignored...
34
+ def qt4_get(parent, title, label, default, target = nil)
35
+
36
+ file = Qt::FileDialog.get_open_file_name(parent, label,
37
+ File.dirname(default),
38
+ filter)
39
+ if file.nil?
40
+ raise CancelInput
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ module Qt4MB
49
+
50
+ # A widget with a nice Browse box to choose
51
+ class FileInputWidget < Qt::Widget
52
+
53
+ # Emitted when the value is changed. Connected to
54
+ # editingFinished().
55
+ signals 'value_changed()'
56
+
57
+ # When the button is clicked
58
+ slots 'button_clicked()'
59
+
60
+ # Creates a Generic widget. _type_ is the FileParameter
61
+ # instance for whom we should do conversion.
62
+ def initialize(parent, type)
63
+ super(parent)
64
+ @type = type
65
+ @layout = Qt::HBoxLayout.new(self)
66
+
67
+ @line_edit = Qt::LineEdit.new
68
+ @layout.add_widget(@line_edit)
69
+ connect(@line_edit, SIGNAL('editingFinished()'),
70
+ SIGNAL('value_changed()'))
71
+
72
+ @button = Qt::PushButton.new("Browse")
73
+ @layout.add_widget(@button)
74
+ connect(@button, SIGNAL('clicked()'),
75
+ SLOT('button_clicked()'))
76
+ end
77
+
78
+ def button_clicked
79
+ file = @type.qt4_get(parent, "dummy",
80
+ "Select file", text)
81
+ self.text=file
82
+ rescue
83
+ # Nothing, just don't set the current value
84
+ end
85
+
86
+ def text
87
+ return @line_edit.text
88
+ end
89
+
90
+ def text=(t)
91
+ @line_edit.setText(t)
92
+ end
93
+
94
+
95
+ def value
96
+ return text
97
+ end
98
+
99
+ def value=(v)
100
+ self.text=v
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,172 @@
1
+ # parameter.rb : Qt support for parameters.
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
+
19
+
20
+
21
+ module MetaBuilder
22
+
23
+ # An exception raised when the user cancels an input dialog box.
24
+ class CancelInput < Exception
25
+ end
26
+
27
+ class ParameterType
28
+
29
+ # Creates a widget suitable for editing the value and returns it.
30
+ # The widget will be a child of _parent_ (can be nil).
31
+ # The _target_ is provided for the Parameter system. It should contain
32
+ # the value of the target whose parameter we should set. This can be
33
+ # used for instance when the value has dynamic content depending on
34
+ # some internals of _target_. Reimplementations should not
35
+ # fail if it is +nil+. The _style_ parameter
36
+ # is for now a placeholder.
37
+ #
38
+ # The created widget must provide the following:
39
+ # * <tt>text=|text</tt>: a writer|reader pair for the string value;
40
+ # * <tt>value=|value</tt>: a writer|reader pair for the real value;
41
+ # * <tt>value_changed()</tt> : a signal for when the value has changed.
42
+ # should be used in the same way as Qt::LineEdit::editingFinished
43
+ # in the sense that it should only trigger on complete output.
44
+ # * <tt>label=</tt>: a way to set the label. Label could be basically
45
+ # anything accepted by Qt; if it is +nil+ or +false+, the display is
46
+ # switched off. The label display must be switched off by default.
47
+ #
48
+ def qt4_create_input_widget(parent = nil, target = nil, style = nil)
49
+ return Qt4MB::GenericInputWidget.new(parent, self)
50
+ end
51
+
52
+ # Pops up a Qt4 dialog box to ask for the value of the parameter.
53
+ # * _parent_ is the parent widget;
54
+ # * _title_ is the title of the Dialog Box
55
+ # * _label_ is the label of the widget used for querying
56
+ # * _default_ is the default value (of the appropriate type).
57
+ # * _target_ is the target for the parameters system. Defaults
58
+ # to +nil+.
59
+ #
60
+ # Returns directly a value in the right type. The default
61
+ # implementation uses the #qt4_create_input_widget function.
62
+ # As +false+ or +nil+ are appropriate return values in some
63
+ # cases, #qt4_get raises a CancelInput exception. You *must*
64
+ # handle it if you don't want nasty surprises...
65
+
66
+ def qt4_get(parent, title, label, default, target = nil)
67
+ return Qt4MB::GenericDialog.get_type(parent, self,
68
+ title, label, default, target)
69
+ end
70
+
71
+ # Converts from the type to a QVariant
72
+ def type_to_variant(value)
73
+ return Qt::Variant.new(value)
74
+ end
75
+
76
+ # Converts from a QVariant to the appropriate (Ruby) type.
77
+ def variant_to_type(variant)
78
+ return variant.value
79
+ end
80
+
81
+ end
82
+
83
+ class Parameter
84
+ end
85
+
86
+ # This module holds all the classes descending from the Qt4 classes.
87
+ module Qt4MB
88
+
89
+
90
+ # THIS MUST BE REWRITTEN TO COMPLY WITH THE NEW SPECS !!!
91
+ # A generic input for the types. Based on Qt::LineEdit.
92
+ class GenericInputWidget < Qt::LineEdit
93
+
94
+ # Emitted when the value is changed. Connected to
95
+ # editingFinished().
96
+ signals 'value_changed()'
97
+
98
+ # Creates a Generic widget. _type_ is the ParameterType child
99
+ # instance for whom we should do conversion.
100
+ def initialize(parent, type)
101
+ super(parent)
102
+ @type = type
103
+ connect(self, SIGNAL('editingFinished()'),
104
+ SIGNAL('value_changed()'))
105
+ end
106
+
107
+ def text=(t)
108
+ setText(t)
109
+ end
110
+
111
+ # Returns the value currently displayed
112
+ def value
113
+ return @type.string_to_type(text)
114
+ end
115
+
116
+ # Sets the display.
117
+ def value=(val)
118
+ self.text = @type.type_to_string(val)
119
+ end
120
+ end
121
+
122
+ class GenericDialog < Qt::Dialog
123
+
124
+ # Creates a generic input dialog for the type _type_.
125
+ def initialize(parent, type, title, label, val, target)
126
+ super(parent)
127
+ self.window_title = title
128
+ vbox = Qt::VBoxLayout.new(self)
129
+
130
+ hbox = Qt::HBoxLayout.new
131
+ @label = Qt::Label.new(label)
132
+ hbox.add_widget(@label)
133
+ @widget = type.qt4_create_input_widget(nil, target)
134
+ hbox.add_widget(@widget)
135
+ @widget.value = val
136
+ vbox.add_layout(hbox)
137
+
138
+ hbox = Qt::HBoxLayout.new
139
+ # OK and Cancel buttons
140
+ button = Qt::PushButton.new("OK")
141
+ connect(button, SIGNAL('clicked()'),
142
+ SLOT('accept()'))
143
+ hbox.add_widget(button)
144
+ button = Qt::PushButton.new("Cancel")
145
+ connect(button, SIGNAL('clicked()'),
146
+ SLOT('reject()'))
147
+ hbox.add_widget(button)
148
+ vbox.add_layout(hbox)
149
+ # and that's all !
150
+ end
151
+
152
+ def exec
153
+ res = super
154
+ if res > 0
155
+ return @widget.value
156
+ else
157
+ raise CancelInput, "User cancelled input"
158
+ end
159
+ end
160
+
161
+ # A convenience wrapper around the functionnality. Creates a
162
+ # GenericDialog and executes it.
163
+ def self.get_type(parent, type, title, label, val, target)
164
+ dialog = new(parent, type, title, label, val, target)
165
+ return dialog.exec
166
+ end
167
+
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -0,0 +1,9 @@
1
+ # parameters.rb: A file grouping all the require necessary to have a
2
+ # working parameter system.
3
+ # This file is copyright 2006 by Vincent Fourmond, but you can do whatever
4
+ # you want with it.
5
+
6
+ require 'MetaBuilder/Qt4/parameter'
7
+ require 'MetaBuilder/Qt4/Parameters/numbers'
8
+ require 'MetaBuilder/Qt4/Parameters/strings'
9
+ require 'MetaBuilder/Qt4/Parameters/dates'
@@ -0,0 +1,603 @@
1
+ # descriptions.rb : The basics of the MetaBuilder system
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 MetaBuilder
19
+
20
+
21
+ # The Description class is a meta-information class that records several
22
+ # informations about the class:
23
+ #
24
+ # * a basic name, code-like, which is used mainly for internal
25
+ # purposes;
26
+ # * a long name, more explanatory, in proper English (or any
27
+ # other language. By the way, it should definitely be
28
+ # translated in a production environment);
29
+ # * a description itself, some small text describing the nature
30
+ # of the class;
31
+ # * a list of all the Parameters that are important for the class,
32
+ # and enough to recreate the state of an object.
33
+ #
34
+ # This class is fairly general, and can be subclassed to fit specific
35
+ # needs. An example is in the SciYAG/Backend system, where the description
36
+ # of a backend is derived from Description.
37
+ #
38
+ # To make use of the Description system for a class, use the following:
39
+ #
40
+ # class SomeClass
41
+ # extend MetaBuilder::DescriptionExtend
42
+ # include MetaBuilder::DescriptionInclude
43
+ #
44
+ # describe 'someclass', 'Some nice class', <<EOD
45
+ # The description of a nice class
46
+ # EOD
47
+ #
48
+ # end
49
+ #
50
+ # Descriptions can be used in two completely different manners:
51
+ #
52
+ # * you can use a single Description to facilitate the interface
53
+ # with the user to query|save parameters;
54
+ # * you can use the Description system to describe a whole set of
55
+ # classes providing similar functions and sharing a base ancestor,
56
+ # such as series of input|output plugins, database formats,
57
+ # themes... In this case, the Description system can act as a real
58
+ # plugin factory, recording every new subclass of the base one
59
+ # and providing facilities to prompt the user.
60
+ #
61
+ # Please note that if you want to use the facilities to dynamically
62
+ # create objects at run-time, the classes used by describe
63
+ # *should not need any parameter for #initialize*.
64
+ #
65
+ #
66
+ # Description provides the following facilities:
67
+ #
68
+ # * a #save_state system, returning a hash containing the current
69
+ # parameters of an object; it's state can be returned to using
70
+ # #restore_state. Please note that the parameters will
71
+ # be set in the same order as they appear in the class. Moreover,
72
+ # if you want the #restore_state to work properly, you must ensure
73
+ # that setting all the parameters restore the state, i.e. you didn't
74
+ # forget anything.
75
+ # * facilities to prepare OptionParser to work on a described class (see
76
+ # #option_parser_fill, #option_parser_banner, #option_parser_options)
77
+ # * facilities to create widgets, dialog boxes and so on to query and
78
+ # modify the contents of a described class instance.
79
+
80
+
81
+ class Description
82
+ # The Class to instantiate.
83
+ attr_accessor :object_class
84
+
85
+ # The name of the class (short, code-like)
86
+ attr_accessor :name
87
+
88
+ # (text) description !
89
+ attr_accessor :description
90
+
91
+ # Long name, the one for public display
92
+ attr_accessor :long_name
93
+
94
+ # The parameter list. The parameters are added when they are found
95
+ # in the class description, and will be used in the order found
96
+ # in this list to recreate the state; beware if one parameter is
97
+ # depending on another one.
98
+ attr_reader :param_list
99
+
100
+ # A hash index on the (short) name and the symbols of the parameters,
101
+ # for quick access. None of those should overlap.
102
+ attr_reader :param_hash
103
+
104
+
105
+ # The list of groups
106
+ attr_accessor :group_list
107
+
108
+ # A hash indexing the groups by their name
109
+ attr_accessor :group_hash
110
+
111
+ # Initializes a Description
112
+ def initialize(cls, name, long_name, description = "")
113
+ @object_class = cls
114
+ @name = name
115
+ @long_name = long_name
116
+ @description = description
117
+ @param_list = []
118
+ @param_hash = {}
119
+ @init_param_list = []
120
+
121
+ @group_list = []
122
+ @group_hash = {}
123
+
124
+ # There is not default group.
125
+ @current_group = nil
126
+ end
127
+
128
+
129
+ # Adds a group to the list, and switches to it
130
+ def add_group(group)
131
+ @group_list << group
132
+ @group_hash[group.name] = group
133
+ @current_group = group
134
+ end
135
+
136
+ # Switches to the named group
137
+ def switch_to_group(name)
138
+ @current_group = @group_hash.fetch(name)
139
+ end
140
+
141
+ # Adds a new parameter to the description
142
+ def add_param(param)
143
+ @param_list << param
144
+
145
+ # Update the parameter hash, for easy access.
146
+ @param_hash[param.reader_symbol] = param
147
+ @param_hash[param.writer_symbol] = param
148
+ @param_hash[param.name] = param
149
+
150
+ # Update the current group, if necessary
151
+ @current_group.add_parameter(param) unless @current_group.nil?
152
+ end
153
+
154
+ # Small set of functions dealing with OptionParsers
155
+
156
+ # Fills an OptionParser with all the parameters the of the class.
157
+ # _instance_ is the instance of the class we want to
158
+ # parametrize, _parser_ is an OptionParser, and if _uniquify_ is set,
159
+ # the description name is prefixed to the parameter name.
160
+ def option_parser_fill(parser, instance, uniquify = true, groups = false)
161
+ option_parser_banner(parser, instance)
162
+ option_parser_options(parser, instance, uniquify, groups)
163
+ end
164
+
165
+ # Fills a parser with options for all parameters. _parser_, _instance_
166
+ # and _uniquify_ have the same meaning as in #option_parser_option.
167
+ # If _grouping_ is set to _true_, the options are organized according
168
+ # to group listing.
169
+ def option_parser_options(parser, instance, uniquify = true,
170
+ grouping = false)
171
+ if grouping
172
+ for group in @group_list
173
+ parser.separator group.long_name
174
+ for param in group.parameter_list
175
+ option_parser_option(parser, param, instance, uniquify)
176
+ end
177
+ end
178
+ else
179
+ for param in @param_list
180
+ option_parser_option(parser, param, instance, uniquify)
181
+ end
182
+ end
183
+ end
184
+
185
+ # Adds an option for the parameter _param_ to the OptionParser _parser_.
186
+ # The corresponding parameter will be set in _instance_. If _uniquify_
187
+ # is set, the description name if prefixed to the option.
188
+ # _param_ can be either a Parameter or a string|symbol registered
189
+ # in #param_hash.
190
+ def option_parser_option(parser, param, instance, uniquify = true)
191
+ raise "The instance is not of the right class" unless
192
+ instance.is_a? @object_class
193
+ if not param.is_a?(Parameter)
194
+ param = param_hash.fetch(param)
195
+ end
196
+ if uniquify
197
+ param_name = "#{@name}-#{param.name}"
198
+ else
199
+ param_name = "#{param.name}"
200
+ end
201
+ param.option_parser_option(parser, param_name, instance)
202
+ end
203
+
204
+ # Subclass Description and reimplement this method to have your own banner.
205
+ def banner(instance)
206
+ return false
207
+ end
208
+
209
+ # Ouputs an OptionParser banner based on the return value
210
+ # of the #banner method.
211
+ def option_parser_banner(parser, instance)
212
+ if b = banner(instance)
213
+ parser.separator b
214
+ end
215
+ end
216
+
217
+ # Creates a hash representing the state of an instance of the
218
+ # described class at a given time. It can be fed to #restore_state
219
+ # and #recreate_state to restore the state to an already existing
220
+ # instance or to create a new one from scratch.
221
+ #
222
+ # This function saves the raw parameters, as there is not much
223
+ # interest in converting to String and back from it.
224
+ #
225
+ # It saves a duplicate of the values, not the values themselves,
226
+ # to make sure it really represents the state of the objects at
227
+ # a given time.
228
+ def save_state(instance)
229
+ ret = {}
230
+ for param in @param_list
231
+ val = param.get_raw(instance)
232
+ ret[param.name] = begin # Necessary as some classes can't be duped.
233
+ val.dup
234
+ rescue
235
+ val # Typical case: nil, which still is a
236
+ # valid value
237
+ end
238
+ end
239
+ return ret
240
+ end
241
+
242
+ # Puts the _target_ into the _state_ returned by #save_state.
243
+ # The parameters are set in the order in which they are found
244
+ # in the class declaration.
245
+ def restore_state(state, target)
246
+ for param in @param_list
247
+ if state.has_key?(param.name)
248
+ param.set_raw(target, state[param.name])
249
+ end
250
+ end
251
+ return target
252
+ end
253
+
254
+ # Returns the default state of an freshly created instance.
255
+ # This way, you can create a well-defined state of one
256
+ # instance via parameters set thus:
257
+ #
258
+ # state = desc.default_state.update({'param' -> "My Value"})
259
+ def default_state
260
+ return save_state(instantiate)
261
+ end
262
+
263
+ # Creates a new (default) instance of the #object_class associated
264
+ # with this Description. Although this method takes additional
265
+ # parameters that are fed to the new method of the class, its
266
+ # use is strongly discouraged.
267
+ def instantiate(*a)
268
+ return @object_class.new(*a)
269
+ end
270
+
271
+ # Creates a new instance of the #object_class associated with
272
+ # this description and fills its state
273
+ def recreate_state(state)
274
+ obj = instantiate
275
+ restore_state(state, obj)
276
+ return obj
277
+ end
278
+
279
+ end
280
+
281
+ # This module should be used in conjunction with DescriptionExtend to
282
+ # provide Descriptions to a class or a family of classes:
283
+ #
284
+ # class NiceClass
285
+ # extend DescriptionExtend
286
+ # include DescriptionInclude
287
+ # # real code now
288
+ # end
289
+ #
290
+ # The DescriptionInclude in itself provide some small facilities: a mehod
291
+ # to access directly the #description of an object (and its #long_name),
292
+ # a series of methods
293
+ # to set|get parameters based on their name (#parameter, #get_param,
294
+ # #get_param_raw, #set_param, #set_param_raw) and some methods to fill
295
+ # an OptionParser (#option_parser_fill, #option_parser_options,
296
+ # #option_parser_banner)
297
+
298
+ module DescriptionInclude
299
+
300
+ # Returns the description associated with the class.
301
+ def description
302
+ return self.class.description
303
+ end
304
+
305
+
306
+ # Returns the long name of the class
307
+ def long_name
308
+ return description.long_name
309
+ end
310
+
311
+ # Small functions to deal with the Parameter objects associated with
312
+ # the class. They are absolutely not necessary to actually access the
313
+ # data, but can come in really useful at some points.
314
+
315
+ # Returns the Parameter object associated with the named parameter
316
+ def parameter(param)
317
+ return description.param_hash.fetch(param)
318
+ end
319
+
320
+ # Query the value of a parameter, returns a String
321
+ def get_param(p)
322
+ return parameter(p).get(self)
323
+ end
324
+
325
+ # Query the value of a parameter, returns the actual value
326
+ def get_param_raw(p)
327
+ return parameter(p).get_raw(self)
328
+ end
329
+
330
+ # Sets the value of a paramete according to a String
331
+ def set_param(param, str)
332
+ return parameter(param).set(self, str)
333
+ end
334
+
335
+ # Sets directly the value of a parameter
336
+ def set_param_raw(param, val)
337
+ return parameter(param).set_raw(self, val)
338
+ end
339
+
340
+ # OptionParser related methods.
341
+
342
+ # Fills an OptionParser with their parameters. Most probably, the
343
+ # default implementation should do for most cases. _uniquify_ asks
344
+ # if we should try to make the command-line options as unique as
345
+ # reasonable to do. If _groups_ is true, organize parameters
346
+ # in groups.
347
+ def option_parser_fill(parser, uniquify = true, groups = false)
348
+ description.option_parser_fill(parser, self,
349
+ uniquify, groups)
350
+ end
351
+
352
+ # Provides only a banner for the current class to the OptionParser
353
+ # _parser_.
354
+ def option_parser_banner(parser)
355
+ description.parser_banner(parser, self)
356
+ end
357
+
358
+ # Prepares an option parser for this instance. See
359
+ # Description#option_parser_options
360
+ def option_parser_options(parser, uniquify = true, groups = false)
361
+ description.option_parser_options(parser, self, uniquify, groups)
362
+ end
363
+
364
+ # SaveState and the like:
365
+
366
+ # Calls Description#save_state on this object
367
+ def save_state
368
+ return description.save_state(self)
369
+ end
370
+
371
+ # Restores a previously saved state. (Well, it doesn't need to
372
+ # be previously saved, you can make it up if you like).
373
+ def restore_state(state)
374
+ description.restore_state(state, self)
375
+ end
376
+
377
+ end
378
+
379
+ # This module should be used with +extend+ to provide the class with
380
+ # descriptions functionnalities. You also need to +include+
381
+ # DescriptionInclude. Please not that all the *instance* methods
382
+ # defined here will become *class* methods in the class you extend.
383
+ #
384
+ # This module defines several methods to add a description (#describe) to a
385
+ # class, to add parameters (#param, #param_noaccess) and to import
386
+ # parameters from parents (#inherit_parameters).
387
+ #
388
+ # Factories can be created using the #craete_factory statement.
389
+ # This makes the
390
+ # current class the factory repository for all the subclasses. It creates
391
+ # a factory class method returning the base factory.
392
+ # You can use #register_class to register the current class into the
393
+ # base factory class.
394
+ module DescriptionExtend
395
+
396
+ # The functions for factory handling.
397
+
398
+ # Makes this class the factory class for all subclasses.
399
+ # It creates four class methods: base_factory, that always
400
+ # points to the closest factory in the hierarchy
401
+ # and three methods used internally.
402
+ #
403
+ # If _auto_ is true, the subclasses are all automatically
404
+ # registered to the factory. If _register_self_ is true
405
+ # the class itself is registered. It is probably not a good
406
+ # idea, so it is off by default.
407
+ def create_factory(auto = true, register_self = false)
408
+ cls = self
409
+ # we create a temporary module so that we can use
410
+ # define_method with a block and extend this class with it
411
+ mod = Module.new
412
+ mod.send(:define_method, :factory) do
413
+ return cls
414
+ end
415
+ mod.send(:define_method, :private_description_list) do
416
+ return @registered_descriptions
417
+ end
418
+ mod.send(:define_method, :private_description_hash) do
419
+ return @registered_descriptions_hash
420
+ end
421
+ # Creates an accessor for the factory class
422
+ mod.send(:define_method, :private_auto_register) do
423
+ @auto_register_subclasses
424
+ end
425
+ self.extend(mod)
426
+
427
+ # Creates the necessary arrays|hashes to handle the registered
428
+ # classes:
429
+ @registered_descriptions = []
430
+ @registered_descriptions_hash = {}
431
+
432
+ @auto_register_subclasses = auto
433
+ end
434
+
435
+ # Checks if the class has a factory
436
+ def has_factory?
437
+ return self.respond_to?(:factory)
438
+ end
439
+
440
+ # Returns the base description if there is one, or nil if there isn't
441
+ def base_description
442
+ if has_factory?
443
+ return factory.description
444
+ else
445
+ return nil
446
+ end
447
+ end
448
+
449
+ # Returns the description list of the factory. Raises an exception if
450
+ # there is no factory
451
+ def factory_description_list
452
+ raise "Must have a factory" unless has_factory?
453
+ return factory.private_description_list
454
+ end
455
+
456
+ # Returns the description hash of the factory. Raises an exception if
457
+ # there is no factory
458
+ def factory_description_hash
459
+ raise "Must have a factory" unless has_factory?
460
+ return factory.private_description_hash
461
+ end
462
+
463
+ # Returns the factory description with the given name
464
+ def factory_description(name)
465
+ raise "Must have a factory" unless has_factory?
466
+ return factory_description_hash.fetch(name)
467
+ end
468
+
469
+ # Returns the Class object associated with the given
470
+ # name in the factory
471
+ def factory_class(name)
472
+ return factory_description(name).object_class
473
+ end
474
+
475
+ # Registers the given description to the factory. If no description
476
+ # is given, the current class is registered.
477
+ def register_class(desc = nil)
478
+ raise "One of the superclasses should have a 'factory' statement" unless
479
+ has_factory?
480
+ desc = description if desc.nil?
481
+ factory_description_list << desc
482
+ factory_description_hash[desc.name] = desc
483
+ end
484
+
485
+ # Returns the Description of the class.
486
+ def description
487
+ return @description
488
+ end
489
+
490
+ # Sets the description of the class. It is probably way better to use
491
+ # #describe, or write your own class method in the base class in the
492
+ # case of a family of classes.
493
+ def set_description(desc)
494
+ @description = desc
495
+ if has_factory? and factory.private_auto_register
496
+ register_class
497
+ end
498
+ end
499
+
500
+
501
+ # Registers an new parameter, with the following properties:
502
+ # * _writer_ is the name of the method used to write that parameter;
503
+ # * _reader_ the name of the method that returns its current value;
504
+ # * _name_ is a short code-like name of the parameter (typically
505
+ # lowercase);
506
+ # * _long_name_ is a more descriptive name, properly capitalized and
507
+ # localized if possible;
508
+ # * _type_ is it's type. Please see the MetaBuilder::ParameterType
509
+ # for a better description of what is a type;
510
+ # * _desc_ is a proper (short) description of the parameter,
511
+ # something that would fit on a What's this box, for instance.
512
+ # * _attrs_ are optional parameters that may come useful, see
513
+ # Parameter#attributes documentation.
514
+ #
515
+ # You might want to use the #param_reader, #param_writer, and
516
+ # #param_accessor facilities that create the respective accessors
517
+ # in addition. A typical example would be:
518
+ #
519
+ # param :set_size, :size, 'size', "Size", {:type => :integer},
520
+ # "The size !!"
521
+ #
522
+ def param(writer, reader, name, long_name, type,
523
+ desc = "", attrs = {})
524
+ raise "Use describe first" if description.nil?
525
+ param = MetaBuilder::Parameter.new(name, writer, reader,
526
+ long_name,
527
+ type, desc, attrs)
528
+ description.add_param(param)
529
+ return param
530
+ end
531
+
532
+ # The same as #param, but creates a attr_reader in addition
533
+ def param_reader(writer, reader, name, long_name, type,
534
+ desc = "", attrs = {})
535
+ p = param(writer, reader, name, long_name, type, desc, attrs)
536
+ attr_reader reader
537
+ return p
538
+ end
539
+
540
+ # The same as #param, except that _writer_ is made from _symbol_ by
541
+ # appending a = at the end. An attr_writer is created for the _symbol_.
542
+ def param_writer(symbol, name, long_name, type,
543
+ desc = "", attrs = {})
544
+ writer = (symbol.to_s + '=').to_sym
545
+ p = param(writer, symbol, name, long_name, type, desc, attrs)
546
+ attr_writer symbol
547
+ return p
548
+ end
549
+
550
+ # The same as #param_writer, except that an attr_writer is created
551
+ # for the _symbol_ instead of only a attr_writer. The most useful of
552
+ # the four methods. Typical use:
553
+ #
554
+ # param_accessor :name, 'name', "Object name", {:type => :string},
555
+ # "The name of the object"
556
+ def param_accessor(symbol, name, long_name, type,
557
+ desc = "", attrs = {})
558
+ writer = (symbol.to_s + '=').to_sym
559
+ p = param(writer, symbol, name, long_name, type, desc, attrs)
560
+ attr_accessor symbol
561
+ return p
562
+ end
563
+
564
+ # Creates a description attached to the current class. It needs to be
565
+ # used before anything else.
566
+ def describe(name, longname = name, desc = "")
567
+ d = Description.new(self, name, longname, desc)
568
+ set_description(d)
569
+ end
570
+
571
+
572
+ # Imports the given parameters directly from the parent class.
573
+ # This function is quite naive and will not look further than
574
+ # the direct #superclass.
575
+ def inherit_parameters(*names)
576
+ if self.superclass.respond_to?(:description)
577
+ parents_params = self.superclass.description.param_hash
578
+ for n in names
579
+ if parents_params.key?(n)
580
+ description.add_param(parents_params[n])
581
+ else
582
+ warn "Param #{n} not found"
583
+ end
584
+ end
585
+ else
586
+ warn "The parent class has no description"
587
+ end
588
+ end
589
+
590
+ # Switches to the given ParameterGroup. If it doesn't exist, it is
591
+ # created with the given parameters.
592
+ def group(name, long_name = name, desc = nil)
593
+ if not description.group_hash.has_key?(name)
594
+ group = ParameterGroup.new(name, long_name, desc)
595
+ description.add_group(group)
596
+ end
597
+ description.switch_to_group(name)
598
+ end
599
+
600
+ end
601
+
602
+ end
603
+