ctioga2 0.10.1 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
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