Skip to content

Commit 30b80bc

Browse files
committed
Add redis to search
1 parent 89291e9 commit 30b80bc

23 files changed

+707
-144
lines changed

assets/css/app.css

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import "tailwindcss/base";
22
@import "tailwindcss/components";
33
@import "tailwindcss/utilities";
4+
@import "./tela.css";
45

56
:root {
67
--link--display: initial;

assets/css/tela.css

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
[visually-hidden] {
2+
position: absolute;
3+
overflow: hidden;
4+
clip: rect(0 0 0 0);
5+
width: 1px;
6+
height: 1px;
7+
margin: -1px;
8+
padding: 0;
9+
border: 0;
10+
}
11+
12+
/* Font */
13+
14+
[font-size="+7"] {
15+
font-size: var(--size-7);
16+
}
17+
[font-size="+6"] {
18+
font-size: var(--size-6);
19+
}
20+
[font-size="+5"] {
21+
font-size: var(--size-5);
22+
}
23+
[font-size="+4"] {
24+
font-size: var(--size-4);
25+
}
26+
[font-size="+3"] {
27+
font-size: var(--size-3);
28+
}
29+
[font-size="+2"] {
30+
font-size: var(--size-2);
31+
}
32+
[font-size="+1"] {
33+
font-size: var(--size-1);
34+
}
35+
[font-size="0"] {
36+
font-size: var(--size-0);
37+
}
38+
[font-size="-1"] {
39+
font-size: var(--size--1);
40+
}
41+
[font-size="-2"] {
42+
font-size: var(--size--2);
43+
}
44+
45+
/* Sizing */
46+
47+
[w-max="md"] {
48+
max-width: 768px;
49+
}
50+
51+
[w-full] {
52+
width: 100%;
53+
}
54+
55+
/* Flex row */
56+
57+
.X,
58+
[x-x] {
59+
display: flex;
60+
flex-direction: row;
61+
justify-content: center;
62+
align-items: center;
63+
}
64+
65+
[x-space="between"] {
66+
justify-content: space-between;
67+
}
68+
69+
[x-space] > * + * {
70+
margin-left: 1rem;
71+
}
72+
[x-space="1/2"] > * + * {
73+
margin-left: 0.5rem;
74+
}
75+
[x-space="1/4"] > * + * {
76+
margin-left: 0.25rem;
77+
}
78+
[x-space="auto"] > * {
79+
margin-left: auto;
80+
margin-right: auto;
81+
}
82+
83+
/* Flex column */
84+
85+
.Y,
86+
[y-y] {
87+
display: flex;
88+
flex-direction: column;
89+
justify-content: center;
90+
align-items: center;
91+
}
92+
93+
[y-stretch] {
94+
align-items: stretch;
95+
}
96+
97+
[y-space] > * + * {
98+
margin-top: 1rem;
99+
}
100+
101+
/* Padding */
102+
103+
[p-t] {
104+
padding-top: 1rem;
105+
}
106+
[p-b] {
107+
padding-bottom: 1rem;
108+
}
109+
[p-l] {
110+
padding-left: 1rem;
111+
}
112+
[p-r] {
113+
padding-right: 1rem;
114+
}
115+
116+
/* MEDIUM BREAKPOINT */
117+
118+
/* Flex column (medium) */
119+
120+
/* 48em ~= 768px */
121+
@media (min-width: 48em) {
122+
.X\>\=48em,
123+
[x-x~="md"] {
124+
display: flex;
125+
flex-direction: row;
126+
justify-content: center;
127+
align-items: center;
128+
}
129+
}

assets/package-lock.json

+5-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/runtime.exs

+5
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ if config_env() == :prod do
6868
#
6969
# See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
7070
end
71+
72+
config :components_guide, :upstash,
73+
redis_url: System.fetch_env!("UPSTASH_REDIS_URL"),
74+
redis_rest_url: System.fetch_env!("UPSTASH_REDIS_REST_URL"),
75+
redis_rest_token: System.fetch_env!("UPSTASH_REDIS_REST_TOKEN")

lib/components_guide/application.ex

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ defmodule ComponentsGuide.Application do
77

88
@impl true
99
def start(_type, _args) do
10+
upstash_config = Application.fetch_env!(:components_guide, :upstash)
11+
redis_url_string = Access.fetch!(upstash_config, :redis_url)
12+
redis_uri = URI.new!(redis_url_string)
13+
IO.inspect(redis_uri)
14+
1015
children = [
1116
# Start the Telemetry supervisor
1217
ComponentsGuideWeb.Telemetry,
@@ -26,7 +31,9 @@ defmodule ComponentsGuide.Application do
2631
%{
2732
id: :research_spec_cache,
2833
start: {Cachex, :start_link, [:research_spec_cache, []]}
29-
}
34+
},
35+
# {Redix, {Access.fetch!(upstash_config, :redis_url), [name: :upstash_redix]}}
36+
# {Redix, {redis_url_string, [name: :upstash_redix]}}
3037
# ComponentsGuide.Worker
3138
]
3239

lib/components_guide/fetch.ex

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule ComponentsGuide.Fetch do
2+
alias __MODULE__.{Get, Request}
3+
4+
def get!(url_string) when is_binary(url_string) do
5+
Get.get_following_redirects!(url_string)
6+
end
7+
8+
def load!(%Request{} = request) do
9+
Get.load!(request)
10+
end
11+
end

lib/components_guide/fetch/get.ex

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
defmodule ComponentsGuide.Fetch.Get do
2+
alias ComponentsGuide.Fetch.{Request, Response}
3+
4+
@timeout 5000
5+
6+
defmodule Timings do
7+
defstruct [:duration, :start]
8+
9+
def start() do
10+
%__MODULE__{
11+
start: System.monotonic_time()
12+
}
13+
end
14+
15+
def finish(timings = %__MODULE__{start: start}) do
16+
duration = System.monotonic_time() - start
17+
put_in(timings.duration, duration)
18+
end
19+
20+
def start_with_telemetry(event_name, metadata \\ %{}) do
21+
t = start()
22+
23+
:telemetry.execute(
24+
event_name,
25+
%{start: t.start},
26+
metadata
27+
)
28+
29+
t
30+
end
31+
32+
def finish_with_telemetry(t = %__MODULE__{}, event_name, metadata \\ %{}) do
33+
t = finish(t)
34+
35+
:telemetry.execute(
36+
event_name,
37+
%{duration: t.duration},
38+
metadata
39+
)
40+
41+
t
42+
end
43+
end
44+
45+
def get_following_redirects!(url_string) when is_binary(url_string) do
46+
request = Request.new!(url_string)
47+
48+
case load!(request) do
49+
%Response{status: status, headers: headers} = resp when status >= 300 and status < 400 ->
50+
case Enum.find(headers, fn {key, _} -> key == "location" end) do
51+
# No redirect!
52+
nil ->
53+
resp
54+
55+
{_, location} ->
56+
IO.puts("Following #{status} redirect to #{location}")
57+
58+
case Request.new(location) do
59+
{:ok, request} ->
60+
# TODO: use existing conn if host is the same.
61+
load!(request)
62+
63+
_ ->
64+
{:error, {:invalid_url, location}}
65+
end
66+
end
67+
68+
other ->
69+
other
70+
end
71+
end
72+
73+
def load!(req = %Request{uri: %URI{host: host, port: 443}}) do
74+
t = Timings.start_with_telemetry([:fetch, :load!, :start], %{req: req})
75+
76+
{:ok, conn} = Mint.HTTP.connect(:https, host, 443, mode: :passive, protocols: [:http1])
77+
{conn, response} = do_request(conn, req)
78+
Mint.HTTP.close(conn)
79+
80+
t =
81+
Timings.finish_with_telemetry(t, [:fetch, :load!, :done], %{
82+
req: req
83+
})
84+
85+
response = Response.add_timings(response, t)
86+
87+
IO.puts(
88+
"Loaded #{req.url_string} in #{System.convert_time_unit(t.duration, :native, :millisecond)}ms. #{inspect(response.done?)}"
89+
)
90+
91+
response
92+
end
93+
94+
def load_many_example(n \\ 2) do
95+
load_many!(
96+
"components.guide",
97+
Enum.map(0..n, fn _ -> Request.new!("https://components.guide/") end)
98+
)
99+
end
100+
101+
def load_many!(host, reqs) when is_binary(host) and is_list(reqs) do
102+
t = Timings.start_with_telemetry([:fetch, :load_many!, :start], %{host: host})
103+
104+
{:ok, conn} = Mint.HTTP.connect(:https, host, 443, mode: :passive, protocols: [:http1])
105+
106+
{conn, results} =
107+
Enum.reduce(reqs, {conn, []}, fn
108+
%Request{uri: %URI{host: ^host, port: 443}} = req, {conn, results} ->
109+
t = Timings.start_with_telemetry([:fetch, :load_many!, :request, :start], %{req: req})
110+
111+
{conn, response} = do_request(conn, req)
112+
113+
t =
114+
Timings.finish_with_telemetry(t, [:fetch, :load_many!, :request, :done], %{
115+
req: req
116+
})
117+
118+
response = Response.add_timings(response, t)
119+
120+
{conn, [response | results]}
121+
end)
122+
123+
Mint.HTTP.close(conn)
124+
results = Enum.reverse(results)
125+
126+
Timings.finish_with_telemetry(t, [:fetch, :load_many!, :done], %{host: host})
127+
results
128+
end
129+
130+
defp recv_all(result = %Response{done?: true}, conn, _request_ref), do: {conn, result}
131+
132+
defp recv_all(result, conn, request_ref) do
133+
case Mint.HTTP.recv(conn, 0, @timeout) do
134+
{:ok, conn, responses} ->
135+
Response.add_responses(result, responses, request_ref)
136+
|> recv_all(conn, request_ref)
137+
138+
{:error, conn, error, _responses} ->
139+
{conn, Response.add_error(result, error)}
140+
end
141+
end
142+
143+
defp do_request(
144+
conn,
145+
%Request{
146+
method: method,
147+
uri: %URI{path: path},
148+
headers: headers,
149+
body: body,
150+
url_string: url_string
151+
}
152+
) do
153+
result = Response.new(url_string)
154+
155+
case Mint.HTTP.request(conn, method, path || "/", headers, body) do
156+
{:error, conn, reason} ->
157+
{conn, Response.add_error(result, reason)}
158+
159+
{:ok, conn, request_ref} ->
160+
recv_all(result, conn, request_ref)
161+
end
162+
end
163+
end

lib/components_guide/fetch/request.ex

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
defmodule ComponentsGuide.Fetch.Request do
2+
defstruct method: "GET", url_string: "", uri: %URI{}, headers: [], body: nil
3+
4+
def new!(url_string, options \\ []) do
5+
headers = Keyword.get(options, :headers, [])
6+
method = Keyword.get(options, :method, "GET")
7+
body = Keyword.get(options, :body)
8+
uri = URI.new!(url_string)
9+
%__MODULE__{method: method, url_string: url_string, uri: uri, headers: headers, body: body}
10+
end
11+
12+
def new(url_string, options \\ []) do
13+
headers = Keyword.get(options, :headers, [])
14+
method = Keyword.get(options, :method, "GET")
15+
body = Keyword.get(options, :body)
16+
17+
with {:ok, uri} <- URI.new(url_string) do
18+
{:ok,
19+
%__MODULE__{method: method, url_string: url_string, uri: uri, headers: headers, body: body}}
20+
else
21+
{:error, _} = value -> value
22+
end
23+
end
24+
end

0 commit comments

Comments
 (0)