Class: Unmagic::Icon::Web

Inherits:
Object
  • Object
show all
Defined in:
lib/unmagic/icon/web.rb

Constant Summary collapse

ALL_LIBRARIES =

The library param value that means “show icons from every library”.

"all"
PAGE_SIZE =

How many icons to send per page (initial render and each “load more”).

100

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.appObject



17
18
19
20
21
22
23
# File 'lib/unmagic/icon/web.rb', line 17

def self.app
  @app ||= Rack::Builder.new do
    public_path = File.expand_path("web/public", __dir__)
    use Rack::Static, urls: [ "/public" ], root: File.dirname(public_path)
    run Unmagic::Icon::Web.new
  end.to_app
end

.call(env) ⇒ Object



13
14
15
# File 'lib/unmagic/icon/web.rb', line 13

def self.call(env)
  app.call(env)
end

Instance Method Details

#asset_url(path) ⇒ Object

Build a static asset URL, honouring any SCRIPT_NAME mount prefix.



118
119
120
# File 'lib/unmagic/icon/web.rb', line 118

def asset_url(path)
  [ @request.env["SCRIPT_NAME"], path ].join.gsub(%r{/+}, "/")
end

#call(env) ⇒ Object



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
# File 'lib/unmagic/icon/web.rb', line 31

def call(env)
  @request = Rack::Request.new(env)
  @libraries = Unmagic::Icon::Library::Registry.all

  @search_query = @request.params["search"].to_s
  @offset = @request.params["offset"].to_i
  @offset = 0 if @offset.negative?
  library_param = @request.params["library"].to_s

  # No `library` param (a fresh page load) defaults to "All".
  if library_param.empty? || library_param == ALL_LIBRARIES
    @selected_library = nil
    @selected_param = ALL_LIBRARIES
    candidates = @libraries.flat_map(&:icons)
  else
    begin
      @selected_library = Unmagic::Icon::Library::Registry.find(library_param)
    rescue Unmagic::Icon::LibraryNotFoundError
      return [ 404, { "content-type" => "text/plain" }, [ "Library not found: #{library_param}" ] ]
    end
    @selected_param = @selected_library.name
    candidates = @selected_library.icons
  end

  # Search and paginate on the server; only the page's icons get their svg
  # read+serialized, so we never inline the whole (possibly huge) set.
  filtered = filter_icons(candidates, @search_query)
  @total = filtered.length
  @page_icons = filtered[@offset, PAGE_SIZE] || []

  return fragment_response if fragment_request?

  template_path = File.expand_path("web/views/layout.html.erb", __dir__)
  template = ERB.new(File.read(template_path))
  html = template.result(binding)

  [ 200, { "content-type" => "text/html; charset=utf-8" }, [ html ] ]
end

#escape(text) ⇒ Object



70
71
72
# File 'lib/unmagic/icon/web.rb', line 70

def escape(text)
  CGI.escapeHTML(text.to_s)
end

#filter_icons(icons, query) ⇒ Object

Case-insensitive substring match on the icon name.



75
76
77
78
79
80
# File 'lib/unmagic/icon/web.rb', line 75

def filter_icons(icons, query)
  return icons if query.empty?

  needle = query.downcase
  icons.select { |icon| icon.name.downcase.include?(needle) }
end

#fragment_request?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/unmagic/icon/web.rb', line 82

def fragment_request?
  @request.params["format"] == "fragment"
end

#fragment_responseObject

The page of icons as a rendered HTML fragment, which the browser appends (“load more”) or swaps in (live search). Counts ride along as headers so the body stays pure markup. Icons are rendered in exactly one place —the ‘icons` partial, shared with the full page.



90
91
92
93
94
95
96
97
98
99
# File 'lib/unmagic/icon/web.rb', line 90

def fragment_response
  headers = {
    "content-type" => "text/html; charset=utf-8",
    "x-total-count" => @total.to_s,
    "x-offset" => @offset.to_s,
    "x-page-size" => PAGE_SIZE.to_s
  }

  [ 200, headers, [ render_icons(@page_icons) ] ]
end

#library_url(library_name) ⇒ Object

Build a URL to the gallery selecting the given library, carrying the current search query along. ‘library_name` of nil selects “All”.



111
112
113
114
115
# File 'lib/unmagic/icon/web.rb', line 111

def library_url(library_name)
  params = { "library" => library_name || ALL_LIBRARIES }
  params["search"] = @search_query unless @search_query.empty?
  url(params)
end

#render_icons(icons) ⇒ Object

Render the shared icons partial for a list of icons. ‘-%>` trim keeps the output empty (not whitespace) when there are no icons, so the browser can detect “no results”.



104
105
106
107
# File 'lib/unmagic/icon/web.rb', line 104

def render_icons(icons)
  template_path = File.expand_path("web/views/icons.html.erb", __dir__)
  ERB.new(File.read(template_path), trim_mode: "-").result(binding)
end