Class: FeedMe::FeedData

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

Direct Known Subclasses

Parser

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag_name, parent, builder) ⇒ FeedData

Returns a new instance of FeedData.



334
335
336
337
338
339
# File 'lib/feedme.rb', line 334

def initialize(tag_name, parent, builder)
  @fm_tag_name = tag_name
  @fm_parent = parent
  @fm_builder = builder
  @data = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object



389
390
391
392
393
394
395
396
397
# File 'lib/feedme.rb', line 389

def method_missing(name, *args)
  result = begin
    call_virtual_method(name, args)
  rescue NameError
    raise if fm_builder.options[:error_on_missing_key]
  end
  result = '' if result.nil? and fm_builder.options[:empty_string_for_nil]
  result
end

Instance Attribute Details

#fm_builderObject (readonly)

Returns the value of attribute fm_builder.



332
333
334
# File 'lib/feedme.rb', line 332

def fm_builder
  @fm_builder
end

#fm_parentObject (readonly)

Returns the value of attribute fm_parent.



332
333
334
# File 'lib/feedme.rb', line 332

def fm_parent
  @fm_parent
end

#fm_tag_nameObject (readonly)

Returns the value of attribute fm_tag_name.



332
333
334
# File 'lib/feedme.rb', line 332

def fm_tag_name
  @fm_tag_name
end

Instance Method Details

#[](key) ⇒ Object



365
366
367
# File 'lib/feedme.rb', line 365

def [](key)
  @data[clean_tag(key)]
end

#[]=(key, value) ⇒ Object



369
370
371
# File 'lib/feedme.rb', line 369

def []=(key, value)
  @data[clean_tag(key)] = value
end

#call_virtual_method(sym, args = [], history = []) ⇒ Object

There are several virtual methods for each attribute/tag.

  1. Tag/attribute name: since tags/attributes are stored as arrays,

the instance variable name is the tag/attribute name followed by ‘_array’. The tag/attribute name is actually a virtual method that returns the first element in the array. If a Proc is passed as the first argument and the array has more than one element, the Proc is used to sort the array before returning the first element.

  1. Aliases: for tags/attributes with aliases, the alias is a virtual

method that simply forwards to the aliased method.

  1. Any name that ends with a ‘?’ returns true if the name without

the ‘?’ is a valid method and has a non-nil value.

  1. Any name that ends with a ‘!’ returns the value of the name

without the ‘!’, modified by the currently active set of bang mods

  1. Tag/attribute name + ‘_value’: returns the content portion of

an element if it has both attributes and content, , or to return the default attribute (defined by the value_tags property). Otherwise equivalent to just the tag/attribute name.

  1. Tag/attribute name + ‘_count’: shortcut for tag/attribute

array.size.

  1. If the tag name is of the form “tag+rel”, the tag having the

specified rel value is returned

Raises:

  • (NameError)


420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/feedme.rb', line 420

def call_virtual_method(sym, args=[], history=[])
  # make sure we don't get stuck in an infinite loop
  history.each do |call|
    if call[0] == fm_tag_name and call[1] == sym
      raise FeedMe::InfiniteCallLoopError.new(sym, history) 
    end
  end
  history << [ fm_tag_name, sym ]
          
  name = clean_tag(sym)
  name_str = name.to_s
  array_key = arrayize(name.to_s)

  result = if key? name
    self[name]
  elsif key? array_key
    array = self[array_key]
    elt = if array.size > 1
      if (!args.empty? && args.first.is_a?(Proc))
        args.first.call(array)
      elsif (fm_builder.value_selectors.key?(name))
        fm_builder.value_selectors[name].call(array)
      elsif !fm_builder.default_value_selector.nil?
        fm_builder.default_value_selector.call(array)
      end
    end
    elt || array.first
  elsif name_str[-1,1] == '?'
    !call_virtual_method(name_str[0..-2], args, history).nil? rescue false
  elsif name_str[-1,1] == '!'
    value = call_virtual_method(name_str[0..-2], args, history)
    transform_value(fm_builder.default_transformation, value)
  elsif name_str =~ /(.+)_values/
    call_virtual_method(arrayize($1), args, history).collect do |value|
      _resolve_value value
    end
  elsif name_str =~ /(.+)_value/
    _resolve_value call_virtual_method($1, args, history)
  elsif name_str =~ /(.+)_count/
    call_virtual_method(arrayize($1), args, history).size
  elsif name_str =~ /(.+)_(.+)/ && fm_builder.transformations.key?($2)
    value = call_virtual_method($1, args, history)
    transform_value(fm_builder.transformations[$2], value)
  elsif name_str.include?('/')    # this is only intended to be used internally 
    value = self
    name_str.split('/').each do |p|
      parts = p.split('_')
      name = clean_tag(parts[0])
      new_args = parts.size > 1 ? parts[1..-1] : args
      value = (value.method(name).call(*new_args) rescue 
        value.call_virtual_method(name, new_args, history)) rescue nil
      break if value.nil?
    end
    value
  elsif name_str.include?('+')
    name_data = name_str.split('+')
    rel = name_data[1]
    value = nil
    call_virtual_method(arrayize(name_data[0]), args, history).each do |elt|
      next unless elt.is_a?(FeedData) and elt.rel?
      value = elt if elt.rel.casecmp(rel) == 0
      break unless value.nil?
    end
    value
  elsif fm_builder.aliases.key? name
    names = fm_builder.aliases[name]
    names = [names] unless names.is_a? Array
    value = nil
    names.each do |name|
      value = (method(name).call(*args) rescue 
        call_virtual_method(name, args, history)) rescue next
      break unless value.nil?
    end
    value
  else
    nil
  end

  raise NameError.new("No such method '#{name}'", name) if result.nil?

  result
end

#delete(key) ⇒ Object



349
350
351
# File 'lib/feedme.rb', line 349

def delete(key)
  @data.delete(clean_tag(key))
end

#eachObject



353
354
355
# File 'lib/feedme.rb', line 353

def each
  @data.each {|key, value| yield(key, value) }
end

#each_with_indexObject



357
358
359
# File 'lib/feedme.rb', line 357

def each_with_index
  @data.each_with_index {|key, value, index| yield(key, value, index) }
end

#idObject

special handling for atom id tags, due to conflict with ruby’s Object#id method



375
376
377
# File 'lib/feedme.rb', line 375

def id
  key?(:id) ? self[:id] : call_virtual_method(:id)
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


341
342
343
# File 'lib/feedme.rb', line 341

def key?(key)
  @data.key?(clean_tag(key))
end

#keysObject



345
346
347
# File 'lib/feedme.rb', line 345

def keys
  @data.keys
end

#sizeObject



361
362
363
# File 'lib/feedme.rb', line 361

def size
  @data.size
end

#to_indented_s(indent_step = 2) ⇒ Object



383
384
385
386
387
# File 'lib/feedme.rb', line 383

def to_indented_s(indent_step=2)
  FeedMe.pretty_to_s(self, indent_step, 0, Proc.new do |key, value| 
    (value.is_a?(Array) && value.size == 1) ? [unarrayize(key), value.first] : [key, value]
  end)
end

#to_sObject



379
380
381
# File 'lib/feedme.rb', line 379

def to_s
  to_indented_s
end

#transform(tag, trans) ⇒ Object

Apply transformations to a tag value. Can either accept a transformation name or an array of transformation function names.



505
506
507
508
509
510
# File 'lib/feedme.rb', line 505

def transform(tag, trans)
  value = call_virtual_method(tag) or return nil
  transformations = trans.is_a?(String) ? 
    fm_builder.transformations[trans] : trans
  transform_value(transformations, value)
end

#transform_value(trans_array, value) ⇒ Object



512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/feedme.rb', line 512

def transform_value(trans_array, value)
  trans_array.each do |t|
    return nil if value.nil?
    
    if t.is_a? String
      value = transform_value(fm_builder.transformations[t], value)
    else
      if t.is_a? Symbol
        t_name = t
        args = []
      elsif t[0].is_a? Array
        raise 'array where symbol expected'
      else
        t_name = t[0]
        args = t[1..-1]
      end
      
      trans = fm_builder.transformation_fns[t_name] or
        raise NoMethodError.new("No such transformation #{t_name}", t_name)
      
      if value.is_a? Array
        value = value.collect do |x| 
          x.nil? ? nil : trans.call(x, *args)
        end.compact
      else  
        value = trans.call(value, *args)
      end
    end
  end
  value
end