Skip to content

Commit

Permalink
Merge branch 'main' into fifo-2
Browse files Browse the repository at this point in the history
  • Loading branch information
joeldrapper committed Feb 18, 2024
2 parents 607afdd + 8bd2c7d commit b64ed48
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,12 @@ Style/MixinUsage:

Style/RedundantDoubleSplatHashBraces:
Enabled: false

Style/OptionalArguments:
Enabled: false

Naming/AsciiIdentifiers:
Enabled: false

Naming/MethodName:
Enabled: false
14 changes: 10 additions & 4 deletions lib/phlex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
require "erb"

module Phlex
autoload :BlackHole, "phlex/black_hole"
autoload :Callable, "phlex/callable"
autoload :ConcurrentMap, "phlex/concurrent_map"
autoload :Context, "phlex/context"
autoload :CSV, "phlex/csv"
autoload :DeferredRender, "phlex/deferred_render"
autoload :ElementClobberingGuard, "phlex/element_clobbering_guard"
autoload :Elements, "phlex/elements"
autoload :HTML, "phlex/html"
autoload :FIFO, "phlex/fifo"
autoload :Helpers, "phlex/helpers"
autoload :HTML, "phlex/html"
autoload :SGML, "phlex/sgml"
autoload :SVG, "phlex/svg"
autoload :Unbuffered, "phlex/unbuffered"
autoload :ConcurrentMap, "phlex/concurrent_map"
autoload :BlackHole, "phlex/black_hole"
autoload :FIFO, "phlex/fifo"

# Included in all Phlex exceptions allowing you to match any Phlex error.
# @example Rescue any Phlex error:
Expand Down Expand Up @@ -48,3 +50,7 @@ def name
end
end
end

def 💪
Phlex
end
93 changes: 93 additions & 0 deletions lib/phlex/csv.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# frozen_string_literal: true

class Phlex::CSV
include Phlex::Callable

def initialize(collection)
@collection = collection
@_headers = []
@_current_row = []
@_current_column_index = 0
@_view_context = nil
@_first = true
end

attr_reader :collection

def call(buffer = +"", view_context: nil)
@_view_context = view_context

each_item do |record|
collection_yielder(record) do |*args, **kwargs|
view_template(*args, **kwargs)

if @_first && render_headers?
buffer << @_headers.map! { |value| escape(value) }.join(",") << "\n"
end

buffer << @_current_row.map! { |value| escape(value) }.join(",") << "\n"
@_current_column_index = 0
@_current_row.clear
end

@_first = false
end

buffer
end

def file_name
nil
end

def content_type
"text/csv"
end

private

def column(header = nil, value)
if @_first
@_headers << header
elsif header != @_headers[@_current_column_index]
raise "Inconsistent header."
end

@_current_row << value
@_current_column_index += 1
end

def each_item(&block)
collection.each(&block)
end

def collection_yielder(record)
yield(record)
end

def template(...)
nil
end

def render_headers?
true
end

def helpers
@_view_context
end

def render(renderable)
renderable.call(view_context: @_view_context)
end

def escape(value)
value = value.to_s

if value.include?('"') || value.include?(",") || value.include?("\n")
%("#{value.gsub('"', '""')}")
else
value
end
end
end
8 changes: 8 additions & 0 deletions lib/phlex/html.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ def unbuffered
self.class.__unbuffered_class__.new(self)
end

def file_name
nil
end

def content_type
"text/html"
end

# This should be extended after all method definitions
extend ElementClobberingGuard
end
Expand Down
8 changes: 8 additions & 0 deletions lib/phlex/svg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,13 @@ class SVG < SGML

# This should be extended after all method definitions
extend ElementClobberingGuard

def content_type
"image/svg+xml"
end

def file_name
nil
end
end
end
77 changes: 77 additions & 0 deletions test/phlex/csv.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

Product = Struct.new(:name, :price)

class Example < Phlex::CSV
def view_template(product)
column("name", product.name)
column("price", product.price)
end
end

class ExampleWithoutHeaders < Example
def render_headers?
false
end
end

describe Phlex::CSV do
it "renders a CSV" do
products = [
Product.new("Apple", 1.00),
Product.new("Banana", 2.00)
]

csv = Example.new(products).call

expect(csv).to be == <<~CSV
name,price
Apple,1.0
Banana,2.0
CSV
end

it "escapes commas" do
product = Product.new("Apple, Inc.", 1.00)
csv = Example.new([product]).call

expect(csv).to be == <<~CSV
name,price
"Apple, Inc.",1.0
CSV
end

it "escapes newlines" do
product = Product.new("Apple\nInc.", 1.00)
csv = Example.new([product]).call

expect(csv).to be == <<~CSV
name,price
"Apple\nInc.",1.0
CSV
end

it "escapes quotes" do
product = Product.new("Apple\"Inc.", 1.00)
csv = Example.new([product]).call

expect(csv).to be == <<~CSV
name,price
"Apple""Inc.",1.0
CSV
end

it "renders without headers" do
products = [
Product.new("Apple", 1.00),
Product.new("Banana", 2.00)
]

csv = ExampleWithoutHeaders.new(products).call

expect(csv).to be == <<~CSV
Apple,1.0
Banana,2.0
CSV
end
end

0 comments on commit b64ed48

Please sign in to comment.