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,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
+