ctioga2 0.10.1 → 0.11

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog +18 -0
  3. data/bin/ctioga2 +28 -0
  4. data/lib/ctioga2/commands/commands.rb +1 -0
  5. data/lib/ctioga2/commands/doc/documentation-commands.rb +13 -0
  6. data/lib/ctioga2/commands/doc/help.rb +3 -2
  7. data/lib/ctioga2/commands/doc/html.rb +48 -0
  8. data/lib/ctioga2/commands/doc/introspection.rb +2 -2
  9. data/lib/ctioga2/commands/general-types.rb +10 -0
  10. data/lib/ctioga2/commands/parsers/file.rb +23 -2
  11. data/lib/ctioga2/data/backends/backends.rb +1 -2
  12. data/lib/ctioga2/data/backends/backends/smath.rb +129 -0
  13. data/lib/ctioga2/data/backends/backends/text.rb +1 -0
  14. data/lib/ctioga2/data/dataset.rb +1 -1
  15. data/lib/ctioga2/data/stack.rb +13 -3
  16. data/lib/ctioga2/graphics/elements.rb +2 -0
  17. data/lib/ctioga2/graphics/elements/containers.rb +3 -1
  18. data/lib/ctioga2/graphics/elements/element.rb +194 -5
  19. data/lib/ctioga2/graphics/elements/gradient-region.rb +5 -2
  20. data/lib/ctioga2/graphics/elements/histogram.rb +7 -2
  21. data/lib/ctioga2/graphics/elements/plot-elements.rb +88 -0
  22. data/lib/ctioga2/graphics/elements/primitive.rb +28 -12
  23. data/lib/ctioga2/graphics/elements/region.rb +6 -1
  24. data/lib/ctioga2/graphics/elements/style-lists.rb +2 -2
  25. data/lib/ctioga2/graphics/elements/subplot.rb +3 -3
  26. data/lib/ctioga2/graphics/elements/tangent.rb +5 -8
  27. data/lib/ctioga2/graphics/generator.rb +10 -0
  28. data/lib/ctioga2/graphics/geometry.rb +96 -0
  29. data/lib/ctioga2/graphics/legends.rb +4 -2
  30. data/lib/ctioga2/graphics/legends/area.rb +12 -4
  31. data/lib/ctioga2/graphics/root.rb +16 -14
  32. data/lib/ctioga2/graphics/styles.rb +5 -2
  33. data/lib/ctioga2/graphics/styles/arrows.rb +5 -0
  34. data/lib/ctioga2/graphics/styles/axes.rb +1 -1
  35. data/lib/ctioga2/graphics/styles/base.rb +95 -14
  36. data/lib/ctioga2/graphics/styles/curve.rb +8 -0
  37. data/lib/ctioga2/graphics/styles/drawable.rb +35 -48
  38. data/lib/ctioga2/graphics/styles/factory.rb +23 -23
  39. data/lib/ctioga2/graphics/styles/fill.rb +268 -0
  40. data/lib/ctioga2/graphics/styles/plot.rb +90 -46
  41. data/lib/ctioga2/graphics/styles/sets.rb +3 -0
  42. data/lib/ctioga2/graphics/styles/{sheet.rb → styles.rb} +70 -160
  43. data/lib/ctioga2/graphics/styles/stylesheet.rb +355 -0
  44. data/lib/ctioga2/graphics/styles/texts.rb +4 -2
  45. data/lib/ctioga2/graphics/styles/ticks.rb +44 -4
  46. data/lib/ctioga2/graphics/subplot-commands.rb +84 -9
  47. data/lib/ctioga2/graphics/types.rb +1 -1
  48. data/lib/ctioga2/graphics/types/dimensions.rb +40 -0
  49. data/lib/ctioga2/graphics/types/grid.rb +21 -5
  50. data/lib/ctioga2/graphics/types/point.rb +2 -1
  51. data/lib/ctioga2/log.rb +5 -1
  52. data/lib/ctioga2/metabuilder/types/styles.rb +11 -7
  53. data/lib/ctioga2/plotmaker.rb +2 -0
  54. data/lib/ctioga2/utils.rb +21 -6
  55. data/lib/ctioga2/version.rb +2 -2
  56. metadata +105 -108
@@ -29,6 +29,8 @@ module CTioga2
29
29
  undef :elements
30
30
  undef :subframe
31
31
 
32
+ define_style 'region'
33
+
32
34
  # The curves which delimit the region
33
35
  attr_accessor :curves
34
36
 
@@ -39,7 +41,10 @@ module CTioga2
39
41
  attr_accessor :reversed_fill_style
40
42
 
41
43
  # Creates a new empty region
42
- def initialize(parent = nil, root = nil)
44
+ def initialize(parent, root, opts)
45
+ setup_style(parent, opts)
46
+ # A reason why we don't use the superclass constructor ?
47
+
43
48
  @parent = parent
44
49
  @clipped = true # clipped by default !
45
50
 
@@ -241,7 +241,7 @@ module CTioga2
241
241
 
242
242
  tdy = txt_dy + box_dy
243
243
 
244
- sets = Styles::CurveStyleFactory::parameters['line_color'].sets
244
+ sets = Styles::CurveStyleFactory::parameters['color'].sets
245
245
 
246
246
  set_names = sets.keys.sort
247
247
 
@@ -306,7 +306,7 @@ module CTioga2
306
306
 
307
307
  tdy = txt_dy + box_dy
308
308
 
309
- sets = Styles::CurveStyleFactory::parameters['marker_marker'].sets
309
+ sets = Styles::CurveStyleFactory::parameters['marker'].sets
310
310
 
311
311
  set_names = sets.keys.sort
312
312
 
@@ -46,8 +46,8 @@ module CTioga2
46
46
  # as #real_do hasn't been entered into.
47
47
  attr_accessor :computed_boundaries
48
48
 
49
- def initialize(parent, root, style)
50
- super(parent, root)
49
+ def initialize(parent, root, opts)
50
+ super(parent, root, opts)
51
51
 
52
52
  @subframe = Types::MarginsBox.new("2.8dy", "2.8dy",
53
53
  "2.8dy", "2.8dy")
@@ -56,7 +56,7 @@ module CTioga2
56
56
 
57
57
  @prev_subframe = nil
58
58
 
59
- @style = style || Styles::PlotStyle.new
59
+ @style = Styles::PlotStyle.new(self)
60
60
 
61
61
  @user_boundaries = {}
62
62
 
@@ -30,12 +30,13 @@ module CTioga2
30
30
  'xextent' => 'float',
31
31
  'yextent' => 'float',
32
32
  'nbavg' => 'integer',
33
- 'base-style' => 'text'
34
- }.update(Styles::ArrowStyle::options_hash)
33
+ } # .update(Styles::ArrowStyle::options_hash)
35
34
 
36
35
  TiogaPrimitiveCall.
37
- primitive("tangent", "tangent", [ 'data-point'],
38
- TangentOptions) do |t, point,options|
36
+ styled_primitive("tangent", "tangent", [ 'data-point'],
37
+ Styles::ArrowStyle,
38
+ 'arrow', [],
39
+ TangentOptions) do |t, point,style,options|
39
40
  options ||= {}
40
41
  nb = options['nbavg'] || 7
41
42
  x = point.x_val(nb)
@@ -81,10 +82,6 @@ module CTioga2
81
82
  options['color'] = $last_curve_style.line.color
82
83
  end
83
84
 
84
- st_name = options['base-style'] || "base"
85
- style = Styles::StyleSheet.style_for(Styles::ArrowStyle ,st_name)
86
- style.set_from_hash(options)
87
-
88
85
  coords = options['tail'] + options['head']
89
86
  style.draw_arrow(t, *coords)
90
87
  end
@@ -85,6 +85,7 @@ module CTioga2
85
85
  plot.style.apply_transforms!(dataset)
86
86
  end
87
87
 
88
+ old_opts = options.dup
88
89
  # Now, we trim options unrelated to the plotting
89
90
  options.delete_if { |k,v|
90
91
  ! Graphics::Styles::
@@ -99,6 +100,15 @@ module CTioga2
99
100
  # over the one specified by
100
101
  # --legend.
101
102
  curve = send(@current_curves, dataset, style)
103
+
104
+ # Here, we update the style from the stylesheet and then
105
+ # again from the options, so that the options provided on
106
+ # the command-line take precedence.
107
+ curve.setup_style(plot, old_opts)
108
+ curve.update_style(curve.curve_style)
109
+ curve.curve_style.
110
+ set_from_hash(@style_factory.hash_name_to_target(options))
111
+
102
112
  curve.curve_style.target = curve
103
113
  end
104
114
  return curve
@@ -0,0 +1,96 @@
1
+ # geometry.rb: various geometry-related utility classes
2
+ # copyright (c) 2014 by 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 (in the COPYING file).
13
+
14
+ require 'ctioga2/utils'
15
+ require 'ctioga2/log'
16
+
17
+ module CTioga2
18
+
19
+ # This module contains all graphical elements of CTioga2
20
+ module Graphics
21
+
22
+
23
+ # A geometry line, ie something that has a starting point and a
24
+ # direction. It is infinite
25
+ class Line
26
+
27
+ # Checks if within the bounds of the line (but not necessarily
28
+ # ON the line)
29
+ def within_bounds?(x, y)
30
+ return true
31
+ end
32
+
33
+ attr_accessor :x, :y, :dx, :dy
34
+
35
+ def initialize(x, y, dx, dy)
36
+ @x = x.to_f
37
+ @y = y.to_f
38
+ @dx = dx.to_f
39
+ @dy = dy.to_f
40
+ end
41
+
42
+ # Returns the X and Y positions of the intersection with the
43
+ # given Line, or false should there be none.
44
+ def intersection(line)
45
+ fact = @dx * line.dy - line.dx * @dy
46
+ rhs = @dy * (line.x - @x) - @dx * (line.y - @y)
47
+ if fact != 0 # There is a unique intersection
48
+ beta = rhs/fact
49
+ nx = line.x + beta * line.dx
50
+ ny = line.y + beta * line.dy
51
+ # elsif rhs == 0
52
+ # # Infinite, we return
53
+ # return
54
+ else
55
+ return false
56
+ end
57
+ return [nx, ny] if (within_bounds?(nx, ny) and
58
+ line.within_bounds?(nx, ny))
59
+ return false
60
+ end
61
+ end
62
+
63
+ # Same as line, but with a beginning and an end
64
+ class Segment < Line
65
+
66
+ attr_accessor :x2, :y2
67
+
68
+ def initialize(x1, y1, x2, y2)
69
+ @x2 = x2
70
+ @y2 = y2
71
+ super(x1, y1, x2 - x1, y2 - y1)
72
+ end
73
+
74
+
75
+ def within_bounds?(x, y)
76
+ return (
77
+ (
78
+ (x - @x) * (x - @x2) <= 0 or
79
+ (x - @x).abs < 1e-15 or
80
+ (x - @x2).abs < 1e-15
81
+ ) and
82
+ ((y - @y) * (y - @y2) <= 0 or
83
+ (y - @y).abs < 1e-15 or
84
+ (y - @y2).abs < 1e-15
85
+ )
86
+ )
87
+ end
88
+
89
+ def to_line()
90
+ return Line.new(@x, @y, @dx, @dy)
91
+ end
92
+ end
93
+ end
94
+
95
+ end
96
+
@@ -96,11 +96,13 @@ they have the same meaning as corresponding ones of
96
96
  {command: define-box-style} with the @frame-@ bit dropped.
97
97
  EOH
98
98
 
99
+ opts = LegendStyleOptions
100
+ opts.merge!(Elements::TiogaElement::StyleBaseOptions)
99
101
  LegendInsideCommand =
100
102
  Cmd.new("legend-inside", nil, "--legend-inside",
101
103
  [ CmdArg.new('aligned-point')],
102
- LegendStyleOptions) do |plotmaker, point, options|
103
- l = Legends::LegendArea.new(:inside)
104
+ opts) do |plotmaker, point, options|
105
+ l = Legends::LegendArea.new(:inside, plotmaker.root_object.current_plot, options)
104
106
  l.legend_position = point
105
107
  plotmaker.root_object.current_plot.legend_area = l
106
108
  l.legend_style.set_from_hash(options)
@@ -35,8 +35,11 @@ module CTioga2
35
35
  # @legend_area for the current Elements::Container, with the
36
36
  # given position.
37
37
  #
38
- # \todo make a subclass for a top-level area ????
39
- class LegendArea
38
+ # \todo make a subclass for a top-level area ????
39
+ #
40
+ # This class is a subclass of Elements::TiogaElement so that it
41
+ # can be styled just like the rest.
42
+ class LegendArea < Elements::TiogaElement
40
43
 
41
44
  # The style of the LegendStorage, a Styles::LegendStorageStyle
42
45
  # object (of course)
@@ -44,14 +47,19 @@ module CTioga2
44
47
 
45
48
  # The type of the legend. Can be :left, :right, :top, :bottom
46
49
  # or :inside
50
+ #
51
+ # @todo Should this move inside the style ?
47
52
  attr_accessor :legend_type
48
53
 
49
54
  # The position of the LegendArea. Only significant when the
50
55
  # type is :inside. A Types::AlignedPoint instance.
51
56
  attr_accessor :legend_position
52
57
 
53
- def initialize(type = :right)
54
- @legend_style = Styles::LegendStorageStyle.new
58
+ define_style 'legend', Styles::LegendStorageStyle
59
+
60
+ def initialize(type = :right, parent = nil, opts = {})
61
+ setup_style(parent, opts)
62
+ @legend_style = get_style()
55
63
  @legend_type = type
56
64
  @legend_position = Types::AlignedPoint.new(0.5,0.5,:frame)
57
65
  end
@@ -48,7 +48,7 @@ module CTioga2
48
48
 
49
49
  @container_stack = []
50
50
 
51
- @legend_area = Legends::LegendArea.new
51
+ @legend_area = Legends::LegendArea.new(:right, nil, {'class' => 'root'})
52
52
 
53
53
  @count_legend_in_page = false
54
54
  # @count_legend_in_page = true
@@ -67,15 +67,17 @@ module CTioga2
67
67
  if @current_container
68
68
  return @current_container
69
69
  else
70
- subplot = Elements::Subplot.new(nil, self, nil)
70
+ subplot = Elements::Subplot.new(nil, self,
71
+ {'id' => 'root' })
71
72
  enter_subobject(subplot)
72
73
  return subplot
73
74
  end
74
75
  end
75
76
 
76
- # Enters into a new Elements::Container, _new_object_.
77
- def enter_subobject(new_object)
78
- if @current_container
77
+ # Enters into a new Elements::Container, _new_object_, and adds
78
+ # it to the current container, unless _add_ is false.
79
+ def enter_subobject(new_object, add = true)
80
+ if @current_container && add
79
81
  @current_container.add_element(new_object)
80
82
  end
81
83
  @current_container = new_object
@@ -135,33 +137,33 @@ module CTioga2
135
137
  #
136
138
  # For the sake of convenience, returns the newly created
137
139
  # Elements::Subplot
138
- def subplot()
140
+ def subplot(opts)
139
141
  if ! @current_container
140
- enter_subobject(Elements::Container.new(nil, self))
142
+ enter_subobject(Elements::Container.new(nil, self, {}))
141
143
  end
142
- subplot = Elements::Subplot.new(@current_container, self, nil)
144
+ subplot = Elements::Subplot.new(@current_container, self, opts)
143
145
  enter_subobject(subplot)
144
146
  return subplot
145
147
  end
146
148
 
147
149
  # This function is the companion of #subplot, but for Region
148
150
  # objects. Returns the newly created Region.
149
- def enter_region
151
+ def enter_region(opts)
150
152
  if ! @current_container
151
- subplot
153
+ subplot({})
152
154
  end
153
- region = Elements::Region.new(@current_container, self)
155
+ region = Elements::Region.new(@current_container, self, opts)
154
156
  enter_subobject(region)
155
157
  return region
156
158
  end
157
159
 
158
160
  # This function is the companion of #subplot, but for GradientRegion
159
161
  # objects. Returns the newly created GradientRegion
160
- def enter_gradient
162
+ def enter_gradient(opts)
161
163
  if ! @current_container
162
- subplot
164
+ subplot({})
163
165
  end
164
- region = Elements::GradientRegion.new(@current_container, self)
166
+ region = Elements::GradientRegion.new(@current_container, self, opts)
165
167
  enter_subobject(region)
166
168
  return region
167
169
  end
@@ -14,6 +14,7 @@
14
14
  require 'ctioga2/graphics/styles/base'
15
15
 
16
16
  require 'ctioga2/graphics/styles/drawable'
17
+ require 'ctioga2/graphics/styles/fill'
17
18
  require 'ctioga2/graphics/styles/arrows'
18
19
  require 'ctioga2/graphics/styles/box'
19
20
  require 'ctioga2/graphics/styles/location'
@@ -31,6 +32,8 @@ require 'ctioga2/graphics/styles/errorbar'
31
32
  require 'ctioga2/graphics/styles/sets'
32
33
  require 'ctioga2/graphics/styles/ticks'
33
34
 
35
+ require 'ctioga2/graphics/styles/curve'
36
+
34
37
  require 'ctioga2/graphics/styles/axes'
35
38
  require 'ctioga2/graphics/styles/map-axes'
36
39
  require 'ctioga2/graphics/styles/background'
@@ -39,9 +42,9 @@ require 'ctioga2/graphics/styles/plot'
39
42
  require 'ctioga2/graphics/styles/legend'
40
43
 
41
44
 
42
- require 'ctioga2/graphics/styles/curve'
43
45
 
44
46
  require 'ctioga2/graphics/styles/factory'
45
47
 
46
48
  # Style sheets
47
- require 'ctioga2/graphics/styles/sheet'
49
+ require 'ctioga2/graphics/styles/stylesheet'
50
+ require 'ctioga2/graphics/styles/styles'
@@ -41,6 +41,11 @@ module CTioga2
41
41
  dict.rename_key('style', 'line_style')
42
42
  dict['head'] = [x2,y2]
43
43
  dict['tail'] = [x1,y1]
44
+ for w in %w(head tail)
45
+ if dict["#{w}_marker"] == false
46
+ dict["#{w}_marker"] = "None"
47
+ end
48
+ end
44
49
  t.show_arrow(dict)
45
50
  end
46
51
 
@@ -146,7 +146,7 @@ minor_tick_length minor_tick_width)
146
146
  end
147
147
  t.show_axis(spec)
148
148
  # Now, we draw axis ticks
149
- if type == Tioga::FigureConstants::AXIS_WITH_MAJOR_TICKS_AND_NUMERIC_LABELS || type == Tioga::FigureConstants::AXIS_WITH_TICKS_AND_NUMERIC_LABELS
149
+ if (type == Tioga::FigureConstants::AXIS_WITH_MAJOR_TICKS_AND_NUMERIC_LABELS) || (type == Tioga::FigureConstants::AXIS_WITH_TICKS_AND_NUMERIC_LABELS)
150
150
 
151
151
  fnc = info['vertical'] ? :convert_figure_to_frame_y : :convert_figure_to_frame_x
152
152
  stl = @tick_label_style.dup
@@ -23,13 +23,53 @@ module CTioga2
23
23
 
24
24
  # This style is the base class of a series of style objects that
25
25
  # share one common feature: all their attributes can be set
26
- # using the set_from_hash function.
26
+ # using the #set_from_hash function.
27
27
  class BasicStyle
28
28
 
29
29
  OldAttrAccessor = method(:attr_accessor)
30
30
 
31
31
  AllStyles = []
32
32
 
33
+ # Returns the list of valid aliases, including those defined
34
+ # in parent classes.
35
+ def self.aliases
36
+ ret = if superclass.respond_to? :aliases
37
+ superclass.aliases
38
+ else
39
+ {}
40
+ end
41
+ if @aliases
42
+ ret.merge!(@aliases)
43
+ end
44
+ return ret
45
+ end
46
+
47
+ def self.normalize_in(name, fmt = "%s")
48
+ name = name.to_s.downcase.gsub('-', '_')
49
+ als = self.aliases
50
+ if als
51
+ for k, v in als
52
+ if fmt % k == name
53
+ name = fmt % v
54
+ break
55
+ end
56
+ end
57
+ end
58
+ return name
59
+ end
60
+
61
+ def self.normalize_out(name)
62
+ return name.gsub('_', '-')
63
+ end
64
+
65
+ def self.normalize_hash(hsh, fmt = "%s")
66
+ ret = {}
67
+ for k,v in hsh
68
+ ret[normalize_in(k, fmt)] = v
69
+ end
70
+ return ret
71
+ end
72
+
33
73
  def self.inherited(cls)
34
74
  AllStyles << cls
35
75
  end
@@ -57,6 +97,10 @@ module CTioga2
57
97
  end
58
98
  end
59
99
 
100
+ def self.defined_aliases
101
+ return @aliases || {}
102
+ end
103
+
60
104
  # Returns the type of all attributes (chaining to the parent
61
105
  # when applicable)
62
106
  def self.attribute_types
@@ -84,29 +128,31 @@ module CTioga2
84
128
  def self.typed_attribute(symbol, type)
85
129
  sym = symbol.to_sym
86
130
  self.attr_accessor(sym)
87
- type = CmdArg.new(type) unless type.respond_to? :string_to_type
131
+ # The unless type.respond_to? :string_to_type seems
132
+ type = CmdArg.new(type) # unless type.respond_to? :string_to_type
88
133
  @attribute_types ||= {}
89
134
  @attribute_types[sym] = type
90
135
  return type
91
136
  end
92
137
 
93
138
  # Define an attribute to be the alias for something else.
94
- def self.alias_for(symbol, target)
95
- target = target.to_sym
96
- if ! @attribute_types[target]
97
- raise "Declaring alias #{symbol} for unexisting target #{target}"
139
+ #
140
+ # @todo Maybe make multiple aliases ?
141
+ def self.alias_for(what, target, define_methods = false)
142
+ target = self.normalize_in(target)
143
+ what = self.normalize_in(what)
144
+ @aliases ||= {}
145
+ @aliases[what] = target
146
+ if define_methods
147
+ alias_method what.to_sym, target.to_sym
148
+ alias_method "#{what}=".to_sym, "#{target}=".to_sym
98
149
  end
99
- symbol = symbol.to_sym
100
- @attribute_types[symbol] = @attribute_types[target]
101
- @attributes << symbol
102
- alias_method symbol, target
103
- alias_method "#{symbol}=".to_sym, "#{target}=".to_sym
104
150
  end
105
151
 
106
152
  # Returns the type of an attribute, or _nil_ if there is no
107
153
  # attribute of that name. Handles sub-styles correctly.
108
154
  def self.attribute_type(symbol, fmt = "%s")
109
- name = symbol.to_s
155
+ name = self.normalize_in(symbol.to_s, fmt)
110
156
 
111
157
  for k,v in attribute_types
112
158
  if (fmt % k.to_s) == name
@@ -122,7 +168,7 @@ module CTioga2
122
168
  for sub in @sub_styles
123
169
  sym, cls, fmt2, fc = *sub
124
170
  f = fmt % fmt2
125
- ret = cls.attribute_type(symbol, f)
171
+ ret = cls.attribute_type(name, f)
126
172
  return ret if ret
127
173
  end
128
174
  end
@@ -179,6 +225,16 @@ module CTioga2
179
225
  end
180
226
  end
181
227
 
228
+ # And now we expand options
229
+ if @aliases
230
+ for k, v in @aliases
231
+ v = key % v
232
+ if ret.key?(v)
233
+ ret[key % k] = ret[v]
234
+ end
235
+ end
236
+ end
237
+
182
238
  return ret
183
239
  end
184
240
 
@@ -201,6 +257,7 @@ module CTioga2
201
257
  # This function returns the number of properties that were
202
258
  # effectively set (including those set in sub-styles)
203
259
  def set_from_hash(hash, name = "%s")
260
+ hash = self.class.normalize_hash(hash, name)
204
261
  nb_set = 0
205
262
  for key_name in self.class.attributes
206
263
  hash_key = name % key_name
@@ -256,11 +313,16 @@ module CTioga2
256
313
  end
257
314
 
258
315
  # Converts to a hash. Does the reverse of #set_from_hash.
316
+ #
317
+ # _nil_ values get stripped off (but not false values, of course).
259
318
  def to_hash(name = "%s")
260
319
  retval = {}
261
320
  for attr in self.class.attributes
262
321
  if instance_variable_defined?("@#{attr}")
263
- retval[name % attr] = instance_variable_get("@#{attr}")
322
+ val = instance_variable_get("@#{attr}")
323
+ if ! val.nil?
324
+ retval[name % attr] = val
325
+ end
264
326
  end
265
327
  end
266
328
  return retval
@@ -271,6 +333,25 @@ module CTioga2
271
333
  set_from_hash(other_object.to_hash)
272
334
  end
273
335
 
336
+ # Converts a hash in text format into a format suitable for
337
+ # feeding to #set_from_hash. Only relevant keys are
338
+ # converted. Keys that exist in the options hash but are not
339
+ # Strings are left untouched
340
+ def self.convert_string_hash(opts, key = "%s")
341
+ cnv = self.options_hash(key)
342
+
343
+ ret = {}
344
+ for k,v in opts
345
+ if cnv.key? k
346
+ if v.is_a? String
347
+ ret[k] = cnv[k].type.string_to_type(v)
348
+ else
349
+ ret[k] = v
350
+ end
351
+ end
352
+ end
353
+ return ret
354
+ end
274
355
  end
275
356
  end
276
357
  end