Module: ImExport

Defined in:
lib/imexport.rb

Class Method Summary collapse

Class Method Details

.import(file_name, options = {}) ⇒ Object



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/imexport.rb', line 2

def self.import(file_name, options = {})
  if options[:class_name] =~ /[^a-zA-Z\:]+/
    raise "#{options[:class_name]} doesn't look like a class name"
  end
  
  ## e.g. :seminar => "seminar" => "Seminar" => Seminar
  model = eval(options[:class_name].to_s.classify)
  
  ## used to check for an existing record. so we do update_attributes()
  # instead of save() in that case
  find_method = "find_by_#{options[:find_by]}"
  
  ## helps to distinguish between real columns and column content
  # could be something like "COLUMN_"
  columns_prefix = options[:db_columns_prefix].to_s
  
  ## for each line, this is how we check for a new column or cont.
  # from the previous line
  scan_regexp = Regexp.new("^\\s*#{columns_prefix}(\\w+)\\: (.*)")
  
  ## table columns --> model attributes mapping
  # if an attribute is not specified and present as a column,
  # it'll try figure it out:
  # { 'title' => :title } - works w/o expicit mapping
  attr_map = options[:map] || {}
  
  ## Read the file
  # we assume it's been created with vertial columns layout (mysql -E ...)
  IO.foreach(file_name) do |line|
    ## mysql -E ... does this:
    # ********* 1. row **********
    # column: value
    # another_column: value
    # ********* 2. row **********
    unless (line =~ /^\*+ \d+\. row \*+$/).nil?
      ## We've got a new row
      unless block_given?
        ## Save previously created object ...
        if !@model.nil? and @model.valid?
          if (s = model.send("#{find_method}", @model.title))
            s.update_attributes(@model.attributes.reject{|k,v| v.nil?})
          else
            @model.save
          end
        else 
          $stderr.puts "\n>>> ERRORS while storing #{options[:class_name]}: #{@model.errors.full_messages.join('; ')}\n#{@model.inspect}" unless @model.nil?
        end
      else
        ## or pass it to a block
        # in this case we don't do any validations
        yield(@model)
      end
      
      # ... then create a new one
      #$stderr.puts "\n###################################"
      @model = model.new
      @last_column_name = nil
      @last_column_content = nil
      next
    end
    
    ## Cont. of the same row.
    ## It's one table column at a time (or more lines).
    ## Parse it and add set the corresponding attribute in the model
    line.chomp!
    column = line.scan(scan_regexp)
    #$stderr.puts column.inspect
    
    unless column.size > 0
      ## this is a column continuation of the previous line
      @last_column_content << '<br/>' << line
      next
    end
    
    ## this is a new model attribute
    # set last attribute in the model if defined
    # we should have at least two items in the array
    column.flatten!
    @last_column_name = column.first
    @last_column_content = column[1].gsub(/\t+/, " ").strip.gsub(/^\n+$/, "").gsub(/\n+/, "<br/>")
    
    # in column-to-model map
    if attr_map.include?(@last_column_name)
      mattr = attr_map[@last_column_name]
      case
        when mattr.kind_of?(Symbol)
          @model.send("#{mattr.to_s}=", @last_column_content)
        when mattr.kind_of?(Proc)
          mattr.call(@last_column_content, @model)
        when mattr.kind_of?(Hash)
          @model.send("#{mattr.keys.first}=", mattr.values.first.call(@last_column_content))
        else
          $stderr.puts "WARNING: don't know how to handle #{mattr.inspect}"
      end
      
    # simple auto-mapping  
    elsif @model.respond_to?(@last_column_name.to_sym)
      @model.send("#{@last_column_name}=", @last_column_content)
      
    # otherwise we don't know how to do it
    else
      $stderr.puts "WARNING: don't know how to set :#{@last_column_name}"
    end
  end # IO.foreach
end