class Phlex::CSV

def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)

def __escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
 case value
ring
mbol
name
to_s
p_whitespace
= value.strip
ape_csv_injection
lue.empty?
er << value
 FORMULA_PREFIXES_MAP[value.getbyte(0)]
e.gsub!('"', '""')
er << '"\'' << value << '"'
 value.match?(escape_regex)
e.gsub!('"', '""')
er << '"' << value << '"'
er << value
 not escaping CSV injection
r << value
not stripping whitespace
ape_csv_injection
_byte = value.getbyte(0)
lue.empty?
er << '""'
 FORMULA_PREFIXES_MAP[first_byte]
er << '"\'' << value.gsub('"', '""') << '"'
 value.match?(escape_regex)
er << '"' << value.gsub('"', '""') << '"'
er << value
 not escaping CSV injection
lue.empty?
er << '""'
 value.match?(escape_regex)
er << '"' << value.gsub('"', '""') << '"'
er << value

def around_row(...)

def around_row(...)
	row_template(...)
	@row_appender.call
end

def call(buffer = +"", context: nil, delimiter: self.delimiter)

def call(buffer = +"", context: nil, delimiter: self.delimiter)
	ensure_escape_csv_injection_configured!
	strip_whitespace = trim_whitespace?
	escape_csv_injection = escape_csv_injection?
	row_buffer = @_row_buffer
	headers = @_headers
	has_yielder = respond_to?(:yielder, true)
	first_row = true
	render_headers = render_headers?
	if delimiter.length != 1
		raise Phlex::ArgumentError.new("Delimiter must be a single character")
	end
	if strip_whitespace
		escape_regex = /[\n"#{delimiter}]/
	else
		escape_regex = /^\s|\s$|[\n"#{delimiter}]/
	end
	if has_yielder
		warn <<~MESSAGE
			Custom yielders are deprecated in Phlex::CSV.
			Please replace your yielder with an `around_row` method.
			You should be able to just rename your yielder method
			and change `yield` to `super`.
		MESSAGE
	end
	row_appender = -> {
		row = row_buffer
		if first_row
			first_row = false
			i = 0
			number_of_columns = row.length
			first_col = true
			while i < number_of_columns
				header, = row[i]
				headers[i] = header
				if render_headers
					if first_col
						first_col = false
					else
						buffer << delimiter
					end
					__escape__(buffer, header, escape_csv_injection:, strip_whitespace:, escape_regex:)
				end
				i += 1
			end
			buffer << "\n" if render_headers
		end
		i = 0
		number_of_columns = row.length
		first_col = true
		while i < number_of_columns
			header, value = row[i]
			unless headers[i] == header
				raise Phlex::RuntimeError.new("Header mismatch at index #{i}: expected #{headers[i]}, got #{header}.")
			end
			if first_col
				first_col = false
			else
				buffer << delimiter
			end
			__escape__(buffer, value, escape_csv_injection:, strip_whitespace:, escape_regex:)
			i += 1
		end
		buffer << "\n"
		row_buffer.clear
	}
	if has_yielder
		each_item do |record|
			yielder(record) do |*a, **k|
				row_template(*a, **k)
				row_appender.call
			end
		end
	else
		@row_appender = row_appender
		each_item do |record|
			around_row(record)
		end
	end
	buffer
end

def column(header = nil, value)

def column(header = nil, value)
uffer << [header, value]

def content_type

def content_type
	"text/csv"
end

def delimiter

def delimiter
	","
end

def each_item(&)

def each_item(&)
ion.each(&)

def ensure_escape_csv_injection_configured!

def ensure_escape_csv_injection_configured!
pe_csv_injection? == UNDEFINED
<<~MESSAGE
eed to define `escape_csv_injection?` in #{self.class.name}.
njection is a security vulnerability where malicious spreadsheet
lae are used to execute code or exfiltrate data when a CSV is opened
spreadsheet program such as Microsoft Excel or Google Sheets.
ore information, see https://owasp.org/www-community/attacks/CSV_Injection
u’re sure this CSV will never be opened in a spreadsheet program,
an *disable* CSV injection escapes:
 escape_csv_injection? = false
is useful when using CSVs for byte-for-byte data exchange between secure systems.
natively, you can *enable* CSV injection escapes at the cost of data integrity:
 escape_csv_injection? = true
ing the CSV injection escapes will prefix with a single quote `'` any
s that start with: `=`, `+`, `-`, `@`, `\\t`, `\\r`
tunately, there is no one-size-fits-all solution to CSV injection.
eed to decide based on your specific use case.
E

def escape_csv_injection?

Override and set to `false` to disable CSV injection escapes or `true` to enable.
def escape_csv_injection?
ED

def filename

def filename
	nil
end

def initialize(collection)

def initialize(collection)
	@collection = collection
	@_row_buffer = []
	@_headers = []
	@_row_appender = nil
end

def method_missing(method_name, ...)

Handle legacy `view_template` method
def method_missing(method_name, ...)
od_name == :row_template && respond_to?(:view_template)
Deprecated: Use `row_template` instead of `view_template` in Phlex CSVs."
lass.alias_method :row_template, :view_template
emplate(...)

def render_headers?

Override and set to `false` to disable rendering headers.
def render_headers?

def respond_to_missing?(method_name, include_private)

Handle legacy `view_template` method
def respond_to_missing?(method_name, include_private)
_name == :row_template && respond_to?(:view_template)) || super

def trim_whitespace?

Override and set to `true` to strip leading and trailing whitespace from values.
def trim_whitespace?