Module: PostgresCopy::ActsAsCopyTarget::CopyMethods

Defined in:
lib/postgres-copy/acts_as_copy_target.rb

Instance Method Summary collapse

Instance Method Details

#copy_from(path_or_io, options = {}) ⇒ Object

Copy data from a CSV that can be passed as a string (the file path) or as an IO object.

  • You can change the default delimiter passing delimiter: ” in the options hash

  • You can map fields from the file to different fields in the table using a map in the options hash

  • For further details on usage take a look at the README.md



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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/postgres-copy/acts_as_copy_target.rb', line 73

def copy_from path_or_io, options = {}
  options = {:delimiter => ",", :format => :csv, :header => true, :quote => '"'}.merge(options)
  options_string = if options[:format] == :binary
                     "BINARY"
                   else
                     quote = options[:quote] == "'" ? "''" : options[:quote]
                     null = options.key?(:null) ? "NULL '#{options[:null]}'" : ''
                     "DELIMITER '#{options[:delimiter]}' QUOTE '#{quote}' #{null} CSV"
                   end
  io = path_or_io.instance_of?(String) ? File.open(path_or_io, 'r') : path_or_io

  if options[:format] == :binary
    columns_list = options[:columns] || []
  elsif options[:header]
    line = io.gets
    columns_list = options[:columns] || line.strip.split(options[:delimiter])
  else
    columns_list = options[:columns]
  end

  table = if options[:table]
            connection.quote_table_name(options[:table])
          else
            quoted_table_name
          end

  columns_list = columns_list.map{|c| options[:map][c.to_s] } if options[:map]
  columns_string = columns_list.size > 0 ? "(\"#{columns_list.join('","')}\")" : ""
  connection.raw_connection.copy_data %{COPY #{table} #{columns_string} FROM STDIN #{options_string}} do
    if options[:format] == :binary
      bytes = 0
      begin
        while line = io.readpartial(10240)
          connection.raw_connection.put_copy_data line
          bytes += line.bytesize
        end
      rescue EOFError
      end
    else
      while line = io.gets do
        next if line.strip.size == 0
        if block_given?
          row = CSV.parse_line(line.strip, {:col_sep => options[:delimiter]})
          yield(row)
          next if row.all?{|f| f.nil? }
          line = CSV.generate_line(row, {:col_sep => options[:delimiter]})
        end
        connection.raw_connection.put_copy_data line
      end
    end
  end
end

#copy_to(path = nil, options = {}) ⇒ Object

Copy data to a file passed as a string (the file path) or to lines that are passed to a block



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/postgres-copy/acts_as_copy_target.rb', line 12

def copy_to path = nil, options = {}
  options = {:delimiter => ",", :format => :csv, :header => true}.merge(options)
  options_string = if options[:format] == :binary
                     "BINARY"
                   else
                     "DELIMITER '#{options[:delimiter]}' CSV #{options[:header] ? 'HEADER' : ''}"
                   end

  if path
    raise "You have to choose between exporting to a file or receiving the lines inside a block" if block_given?
    connection.execute "COPY (#{self.all.to_sql}) TO #{sanitize(path)} WITH #{options_string}"
  else
    connection.raw_connection.copy_data "COPY (#{self.all.to_sql}) TO STDOUT WITH #{options_string}" do
      while line = connection.raw_connection.get_copy_data do
        yield(line) if block_given?
      end
    end
  end
  return self
end

#copy_to_enumerator(options = {}) ⇒ Object

Create an enumerator with each line from the CSV. Note that using this directly in a controller response will perform very poorly as each line will get put into its own chunk. Joining every (eg) 100 rows together is much, much faster.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/postgres-copy/acts_as_copy_target.rb', line 38

def copy_to_enumerator(options={})
  buffer_lines = options.delete(:buffer_lines)
  # Somehow, self loses its scope once inside the Enumerator
  scope = self.current_scope || self
  result = Enumerator.new do |y|
    scope.copy_to(nil, options) do |line|
      y << line
    end
  end

  if buffer_lines.to_i > 0
    Enumerator.new do |y|
      result.each_slice(buffer_lines.to_i) do |slice|
        y << slice.join
      end
    end
  else
    result
  end
end

#copy_to_string(options = {}) ⇒ Object

Copy all data to a single string



60
61
62
63
64
65
66
67
# File 'lib/postgres-copy/acts_as_copy_target.rb', line 60

def copy_to_string options = {}
  data = ''
  self.copy_to(nil, options){|l| data << l }
  if options[:format] == :binary
    data.force_encoding("ASCII-8BIT")
  end
  data
end