Class: SVGPlot::SVGTag

Inherits:
Object
  • Object
show all
Defined in:
lib/svgplot/plot.rb

Direct Known Subclasses

SVGTagWithParent

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag, attributes = {}, &block) ⇒ SVGTag

Returns a new instance of SVGTag.



6
7
8
9
10
11
12
13
14
# File 'lib/svgplot/plot.rb', line 6

def initialize(tag, attributes={}, &block)
  @tag = validate_tag(tag)
  @attributes = validate_attributes(attributes)
  @children = []

  if block
    instance_exec &block
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/svgplot/plot.rb', line 231

def method_missing(meth, *args, &block)
  #if method is a setter or a getter, check valid attributes:
  check = /^(?<name>.*)(?<op>=|\?)$/.match(meth)
  if check
    raise "Passing a code block to setter or getter is not permited!" if block
    name = validate_attribute(check[:name].to_sym)
    if check[:op] == '?'
      @attributes[name]
    elsif check[:op] == '='
      raise "Setting an attribute with multiple values is not permited!" if args.size > 1
      @attributes[name] = args[0]
    end
  elsif child = validate_child_name(meth)
    spawn_child(child, *args, &block)
  else
    super
  end
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



4
5
6
# File 'lib/svgplot/plot.rb', line 4

def attributes
  @attributes
end

#childrenObject (readonly)

Returns the value of attribute children.



4
5
6
# File 'lib/svgplot/plot.rb', line 4

def children
  @children
end

#tagObject (readonly)

Returns the value of attribute tag.



4
5
6
# File 'lib/svgplot/plot.rb', line 4

def tag
  @tag
end

Instance Method Details

#append_child(child) ⇒ Object



183
184
185
186
187
# File 'lib/svgplot/plot.rb', line 183

def append_child(child)
  @children.push(child)
  child.push_defaults(merge_defaults()) if @defaults
  child
end

#linearGradient(id, attributes = {}, if_exists = :skip, &block) ⇒ Object

special case for linearGradient



131
132
133
134
# File 'lib/svgplot/plot.rb', line 131

def linearGradient(id, attributes={}, if_exists = :skip, &block)
  raise "image reference isn't set, cannot use 'defs' (and thus linearGradient) !" if @img.nil?
  @img.add_def(id, SVGPlot::SVGLinearGradient.new(@img, attributes), if_exists, &block)
end

#matrix(a, b, c, d, e, f) ⇒ Object



45
46
47
48
# File 'lib/svgplot/plot.rb', line 45

def matrix(a, b, c, d, e, f)
  add_transform(:matrix, "#{a}, #{b}, #{c}, #{d}, #{e}, #{f}")
  self
end

#merge_defaultsObject



190
191
192
193
194
195
# File 'lib/svgplot/plot.rb', line 190

def merge_defaults()
  result = {}
  return result if @defaults.empty?
  @defaults.each { |d| result.merge!(d) }
  result
end

#path(attributes = {}, &block) ⇒ Object

special case for path block



119
120
121
# File 'lib/svgplot/plot.rb', line 119

def path(attributes = {}, &block)
  append_child SVGPlot::SVGPath.new(@img, attributes, &block)
end

#pop_defaultsObject



204
205
206
# File 'lib/svgplot/plot.rb', line 204

def pop_defaults()
  @defaults.pop()
end

#push_defaults(defaults) ⇒ Object



198
199
200
201
# File 'lib/svgplot/plot.rb', line 198

def push_defaults(defaults)
  @defaults = [] unless @defaults
  @defaults.push(defaults)
end

#radialGradient(id, attributes = {}, if_exists = :skip, &block) ⇒ Object

special case for radialGradient



138
139
140
141
# File 'lib/svgplot/plot.rb', line 138

def radialGradient(id, attributes={}, if_exists = :skip, &block)
  raise "image reference isn't set, cannot use 'defs' (and thus radialGradient) !" if @img.nil?
  @img.add_def(id, SVGPlot::SVGRadialGradient.new(@img, attributes), if_exists, &block)
end

#raw(data) ⇒ Object

special case for raw blocks.



114
115
116
# File 'lib/svgplot/plot.rb', line 114

def raw(data)
  append_child SVGPlot::SVGRaw.new(@img, data)
end

#rotate(angle, cx = nil, cy = nil) ⇒ Object



30
31
32
33
# File 'lib/svgplot/plot.rb', line 30

def rotate(angle, cx = nil, cy = nil)
  add_transform(:rotate, "#{angle}#{(cx.nil? or cy.nil?) ? "" : ", #{cx}, #{cy}"}")
  self
end

#scale(sx, sy = 1) ⇒ Object



25
26
27
28
# File 'lib/svgplot/plot.rb', line 25

def scale(sx, sy = 1)
  add_transform(:scale, "#{sx}, #{sy}")
  self
end

#skewX(angle) ⇒ Object



35
36
37
38
# File 'lib/svgplot/plot.rb', line 35

def skewX(angle)
  add_transform(:skewX, "#{angle}")
  self
end

#skewY(angle) ⇒ Object



40
41
42
43
# File 'lib/svgplot/plot.rb', line 40

def skewY(angle)
  add_transform(:skewY, "#{angle}")
  self
end

#spawn_child(tag, *args, &block) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/svgplot/plot.rb', line 146

def spawn_child(tag, *args, &block)
  #expected args: nil, [hash], [...]
  parameters = {} if args.size == 0

  unless parameters #are empty
    parameters = args[0] if args[0].is_a? Hash
  end

  unless parameters #are set
    #try to find args expansion rule
    expansion = SVGPlot::SVG_EXPANSION[tag.to_sym]
    raise "Unnamed parameters for #{tag} are not allowed!" unless expansion
  
    if expansion.is_a? Array
      raise "Bad unnamed parameter count for #{tag}, expecting #{expansion.size} got #{if args.last.is_a? Hash then args.size-1 else args.size end}" unless (args.size == expansion.size and not args.last.is_a? Hash) or (args.size - 1 == expansion.size and args.last.is_a? Hash)
      parameters = Hash[expansion.zip(args)]
      if args.last.is_a? Hash
        parameters.merge! args.last
      end
    elsif expansion.is_a? Proc
      hash = args.pop if args.last.is_a? Hash
      parameters = expansion.call(args)
      parameters.merge! hash if hash
    else
      raise "Unexpected expansion mechanism: #{expansion.class}"
    end
  end

  # add default parameters if they are not overwritten
  merge_defaults().each do |key, value|
    parameters[key] = value unless parameters[key]
  end if @defaults

  append_child(SVGPlot::SVGTagWithParent.new(@img, tag, parameters, &block))
end

#to_sObject



276
277
278
279
280
# File 'lib/svgplot/plot.rb', line 276

def to_s
  str = ""
  write(str)
  return str
end

#translate(tx, ty = 0) ⇒ Object

Provide methods for SVG transformations



20
21
22
23
# File 'lib/svgplot/plot.rb', line 20

def translate(tx, ty = 0)
  add_transform(:translate, "#{tx}, #{ty}")
  self
end

#use(id, attributes = {}) ⇒ Object

special case for use block



124
125
126
127
# File 'lib/svgplot/plot.rb', line 124

def use(id, attributes = {})
  id = id.attributes[:id] if id.is_a? SVGPlot::SVGTag
  append_child SVGPlot::SVGTagWithParent.new(@img, "use", attributes.merge("xlink:href" => "##{id}"))
end

#validate_attribute(attribute) ⇒ Object



85
86
87
88
# File 'lib/svgplot/plot.rb', line 85

def validate_attribute(attribute)
  raise "#{@tag} does not support attribute #{attribute}" unless SVGPlot::SVG_STRUCTURE[@tag.to_sym][:attributes].include?(attribute.to_sym)
  attribute.to_sym
end

#validate_attributes(attributes) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/svgplot/plot.rb', line 55

def validate_attributes(attributes)
  clean_attributes = {}

  transforms = {}
  attributes.delete(:transform) { Hash.new }.each do |key, value|
    transforms[key] = value
  end
  unless transforms.empty?
    str = ""
    write_transforms(transforms, str)
    clean_attributes[validate_attribute(:transform)] = str
  end

  styles = {}
  attributes.delete(:style) { Hash.new }.each { |k, v| styles[k] = v }
  unless styles.empty?
    clean_attributes[validate_attribute(:style)] = styles
  end

  attributes.delete(:data) { Hash.new }.each do |key, value|
    clean_attributes["data-#{key.to_s}".to_sym] = value
  end

  attributes.each do |key, value|
    clean_attributes[validate_attribute(key)] = value
  end

  clean_attributes
end

#validate_child_name(name) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
# File 'lib/svgplot/plot.rb', line 218

def validate_child_name(name)
  #aliases the name (like, group instead of g)
  name = SVGPlot::SVG_ALIAS[name.to_sym] if SVGPlot::SVG_ALIAS[name.to_sym]

  #raises only if given name is an actual svg tag. In other case -- assumes user just mistyped.
  if SVGPlot::SVG_STRUCTURE[@tag.to_sym][:elements].include?(name.to_sym)
    name.to_sym
  elsif SVGPlot::SVG_ELEMENTS.include?(name.to_sym)
    raise "#{@tag} should not contain child #{name}" 
  end
end

#validate_tag(tag) ⇒ Object



50
51
52
53
# File 'lib/svgplot/plot.rb', line 50

def validate_tag(tag)
  raise "#{tag} is not a valid tag" unless SVGPlot::SVG_ELEMENTS.include?(tag.to_sym)
  tag.to_sym
end

#with_style(style = {}, &proc) ⇒ Object



209
210
211
212
213
214
215
# File 'lib/svgplot/plot.rb', line 209

def with_style(style={}, &proc)
  push_defaults(style)
  # Call the block
  self.instance_exec(&proc)
  # Pop style again to revert changes
  pop_defaults()
end

#write(output) ⇒ Object



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/svgplot/plot.rb', line 251

def write(output)
  raise "Can not write to given output!" unless output.respond_to?(:<<)
  output << "<#{@tag.to_s}"
  @attributes.each do
    |attribute, value|
    output << " #{attribute.to_s}=\""
    if attribute == :style
      write_styles(value, output)
    elsif attribute == :points
      write_points(value, output)
    else
      output << "#{value.to_s}"
    end
    output << "\""
  end

  if @children.empty?
    output << "/>"
  else
    output << ">"
    @children.each { |c| c.write(output) }
    output << "</#{@tag.to_s}>"
  end  
end

#write_points(points, output) ⇒ Object



105
106
107
108
109
110
111
# File 'lib/svgplot/plot.rb', line 105

def write_points(points, output)
  points.each_with_index do |value, index|
    output << value.to_s
    output << ',' if index.even?
    output << ' ' if (index.odd? and (index != points.size-1))
  end
end

#write_styles(styles, output) ⇒ Object



90
91
92
93
94
95
96
# File 'lib/svgplot/plot.rb', line 90

def write_styles(styles, output)
  styles.each do |attribute, value|
    attribute = attribute.to_s
    attribute.gsub!('_','-')
    output << "#{attribute}:#{value};"
  end
end

#write_transforms(transforms, output) ⇒ Object



98
99
100
101
102
103
# File 'lib/svgplot/plot.rb', line 98

def write_transforms(transforms, output)
  transforms.each do |attribute, value|
    value = [value] unless value.is_a?(Array)
    output << "#{attribute.to_s}(#{value.join(',')}) "
  end
end