CINXE.COM
Generic Actions in Rails 3
<!DOCTYPE html> <html lang="en-US" class="auto" > <head> <title>Generic Actions in Rails 3</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="HandheldFriendly" content="True"/> <link rel="apple-touch-icon" href="https://yehudakatz.com/content/images/2024/08/yehuda-square.png"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="https://yehudakatz.com/assets/built/screen.min.css?v=4365bbbe46"> <script> function colored(e){document.querySelectorAll("."+e).forEach(function(e){e.textContent.includes("✦")&&(e.innerHTML=e.innerHTML.replace("✦","<span>✦</span>"))})} const htmlElement=document.documentElement,classList=htmlElement.classList; window.matchMedia("(prefers-color-scheme: dark)").matches?(classList.add("dark"),localStorage.setItem("theme","dark")):(classList.remove("dark"),localStorage.setItem("theme","light")); document.addEventListener("DOMContentLoaded",()=>{"dark"===localStorage.getItem("theme")?classList.add("dark"):classList.remove("dark")}),window.addEventListener("storage",()=>{"dark"===localStorage.getItem("theme")?classList.add("dark"):classList.remove("dark")}); window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",e=>{e.matches?(classList.add("dark"),localStorage.setItem("theme","dark")):(classList.remove("dark"),localStorage.setItem("theme","light"))});function initializeDarkMode(){var e=document.querySelectorAll(".light-logo"),o=document.querySelectorAll(".footer-light-logo");document.querySelector("html");var r=window.matchMedia("(prefers-color-scheme: dark)");function t(){r.matches?(e.forEach(function(e){e.src=""}),o.forEach(function(e){e.src=""})):(e.forEach(function(e){e.src="https://yehudakatz.com/content/images/2024/08/wycats--2-.png"}),o.forEach(function(e){e.src="https://yehudakatz.com/content/images/2024/08/wycats--2-.png"}))}t(),r.addEventListener("change",t)} </script> <link rel="icon" href="https://yehudakatz.com/content/images/size/w256h256/2024/08/yehuda-square.png" type="image/png"> <link rel="canonical" href="https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/"> <meta name="referrer" content="no-referrer-when-downgrade"> <link rel="amphtml" href="https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/amp/"> <meta property="og:site_name" content="Katz Got Your Tongue"> <meta property="og:type" content="article"> <meta property="og:title" content="Generic Actions in Rails 3"> <meta property="og:description" content="So Django has an interesting feature called "generic views", which essentially allow you to to render a template with generic code. In Rails, the same feature would be called "generic actions" (just a terminology difference). This was possible, but somewhat difficult in Rails 2.x, but it's a breeze in"> <meta property="og:url" content="https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/"> <meta property="article:published_time" content="2009-12-20T23:06:54.000Z"> <meta property="article:modified_time" content="2009-12-21T00:14:18.000Z"> <meta name="twitter:card" content="summary"> <meta name="twitter:title" content="Generic Actions in Rails 3"> <meta name="twitter:description" content="So Django has an interesting feature called "generic views", which essentially allow you to to render a template with generic code. In Rails, the same feature would be called "generic actions" (just a terminology difference). This was possible, but somewhat difficult in Rails 2.x, but it's a breeze in"> <meta name="twitter:url" content="https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/"> <meta name="twitter:label1" content="Written by"> <meta name="twitter:data1" content="Yehuda Katz"> <meta name="twitter:site" content="@wycats"> <meta name="twitter:creator" content="@wycats"> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "publisher": { "@type": "Organization", "name": "Katz Got Your Tongue", "url": "https://yehudakatz.com/", "logo": { "@type": "ImageObject", "url": "https://yehudakatz.com/content/images/2024/08/wycats--2-.png" } }, "author": { "@type": "Person", "name": "Yehuda Katz", "image": { "@type": "ImageObject", "url": "https://yehudakatz.com/content/images/2021/04/Profile---Correct.png", "width": 500, "height": 500 }, "url": "https://yehudakatz.com/author/wycats/", "sameAs": [ "https://twitter.com/wycats" ] }, "headline": "Generic Actions in Rails 3", "url": "https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/", "datePublished": "2009-12-20T23:06:54.000Z", "dateModified": "2009-12-21T00:14:18.000Z", "description": "So Django has an interesting feature called "generic views", which essentially\nallow you to to render a template with generic code. In Rails, the same feature\nwould be called "generic actions" (just a terminology difference).\n\nThis was possible, but somewhat difficult in Rails 2.x, but it's a breeze in\nRails 3.\n\nLet's take a look at a simple generic view in Django, the "redirect_to" view:\n\nurlpatterns = patterns('django.views.generic.simple',\n ('^foo/(?P<id>\\d+)/$', 'redirect_to', {'url': '/b", "mainEntityOfPage": "https://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/" } </script> <meta name="generator" content="Ghost 5.101"> <link rel="alternate" type="application/rss+xml" title="Katz Got Your Tongue" href="https://yehudakatz.com/rss/"> <script defer src="https://cdn.jsdelivr.net/ghost/portal@~2.46/umd/portal.min.js" data-i18n="true" data-ghost="https://yehudakatz.com/" data-key="7d10c674ca44f7fe0a608ff2fb" data-api="https://yehudakatz.ghost.io/ghost/api/content/" data-locale="en-US" crossorigin="anonymous"></script><style id="gh-members-styles">.gh-post-upgrade-cta-content, .gh-post-upgrade-cta { display: flex; flex-direction: column; align-items: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; text-align: center; width: 100%; color: #ffffff; font-size: 16px; } .gh-post-upgrade-cta-content { border-radius: 8px; padding: 40px 4vw; } .gh-post-upgrade-cta h2 { color: #ffffff; font-size: 28px; letter-spacing: -0.2px; margin: 0; padding: 0; } .gh-post-upgrade-cta p { margin: 20px 0 0; padding: 0; } .gh-post-upgrade-cta small { font-size: 16px; letter-spacing: -0.2px; } .gh-post-upgrade-cta a { color: #ffffff; cursor: pointer; font-weight: 500; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a:hover { color: #ffffff; opacity: 0.8; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a.gh-btn { display: block; background: #ffffff; text-decoration: none; margin: 28px 0 0; padding: 8px 18px; border-radius: 4px; font-size: 16px; font-weight: 600; } .gh-post-upgrade-cta a.gh-btn:hover { opacity: 0.92; }</style><script async src="https://js.stripe.com/v3/"></script> <script defer src="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/sodo-search.min.js" data-key="7d10c674ca44f7fe0a608ff2fb" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/main.css" data-sodo-search="https://yehudakatz.ghost.io/" data-locale="en-US" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/ghost/announcement-bar@~1.1/umd/announcement-bar.min.js" data-announcement-bar="https://yehudakatz.com/" data-api-url="https://yehudakatz.com/members/api/announcement/" crossorigin="anonymous"></script> <link href="https://yehudakatz.com/webmentions/receive/" rel="webmention"> <script defer src="/public/cards.min.js?v=4365bbbe46"></script> <link rel="stylesheet" type="text/css" href="/public/cards.min.css?v=4365bbbe46"> <script defer src="/public/member-attribution.min.js?v=4365bbbe46"></script><style>:root {--ghost-accent-color: #ff9b36;}</style> <style> :root { --vp-code-line-height: 1.7; --vp-code-font-size: .875em; --vp-code-color: var(--vp-c-brand-1); --vp-code-link-color: var(--vp-c-brand-1); --vp-code-link-hover-color: var(--vp-c-brand-2); --vp-code-bg: var(--vp-c-default-soft); --vp-code-block-color: var(--vp-c-text-2); --vp-code-block-bg: var(--vp-c-bg-alt); --vp-code-block-divider-color: var(--vp-c-gutter); --vp-code-lang-color: var(--vp-c-text-3); --vp-code-line-highlight-color: var(--vp-c-default-soft); --vp-code-line-number-color: var(--vp-c-text-3); --vp-code-line-diff-add-color: var(--vp-c-success-soft); --vp-code-line-diff-add-symbol-color: var(--vp-c-success-1); --vp-code-line-diff-remove-color: var(--vp-c-danger-soft); --vp-code-line-diff-remove-symbol-color: var(--vp-c-danger-1); --vp-code-line-warning-color: var(--vp-c-warning-soft); --vp-code-line-error-color: var(--vp-c-danger-soft); --vp-code-copy-code-border-color: var(--vp-c-divider); --vp-code-copy-code-bg: var(--vp-c-bg-soft); --vp-code-copy-code-hover-border-color: var(--vp-c-divider); --vp-code-copy-code-hover-bg: var(--vp-c-bg); --vp-code-copy-code-active-text: var(--vp-c-text-2); --vp-code-copy-copied-text-content: "Copied"; --vp-code-tab-divider: var(--vp-code-block-divider-color); --vp-code-tab-text-color: var(--vp-c-text-2); --vp-code-tab-bg: var(--vp-code-block-bg); --vp-code-tab-hover-text-color: var(--vp-c-text-1); --vp-code-tab-active-text-color: var(--vp-c-text-1); --vp-code-tab-active-bar-color: var(--vp-c-brand-1); } :root { --vp-c-white: #ffffff; --vp-c-black: #000000; --vp-c-neutral: var(--vp-c-black); --vp-c-neutral-inverse: var(--vp-c-white) } .dark { --vp-c-neutral: var(--vp-c-white); --vp-c-neutral-inverse: var(--vp-c-black) } :root { --vp-c-gray-1: #dddde3; --vp-c-gray-2: #e4e4e9; --vp-c-gray-3: #ebebef; --vp-c-gray-soft: rgba(142, 150, 170, .14); --vp-c-indigo-1: #3451b2; --vp-c-indigo-2: #3a5ccc; --vp-c-indigo-3: #5672cd; --vp-c-indigo-soft: rgba(100, 108, 255, .14); --vp-c-purple-1: #6f42c1; --vp-c-purple-2: #7e4cc9; --vp-c-purple-3: #8e5cd9; --vp-c-purple-soft: rgba(159, 122, 234, .14); --vp-c-green-1: #18794e; --vp-c-green-2: #299764; --vp-c-green-3: #30a46c; --vp-c-green-soft: rgba(16, 185, 129, .14); --vp-c-yellow-1: #915930; --vp-c-yellow-2: #946300; --vp-c-yellow-3: #9f6a00; --vp-c-yellow-soft: rgba(234, 179, 8, .14); --vp-c-red-1: #b8272c; --vp-c-red-2: #d5393e; --vp-c-red-3: #e0575b; --vp-c-red-soft: rgba(244, 63, 94, .14); --vp-c-sponsor: #db2777 } .dark { --vp-c-gray-1: #515c67; --vp-c-gray-2: #414853; --vp-c-gray-3: #32363f; --vp-c-gray-soft: rgba(101, 117, 133, .16); --vp-c-indigo-1: #a8b1ff; --vp-c-indigo-2: #5c73e7; --vp-c-indigo-3: #3e63dd; --vp-c-indigo-soft: rgba(100, 108, 255, .16); --vp-c-purple-1: #c8abfa; --vp-c-purple-2: #a879e6; --vp-c-purple-3: #8e5cd9; --vp-c-purple-soft: rgba(159, 122, 234, .16); --vp-c-green-1: #3dd68c; --vp-c-green-2: #30a46c; --vp-c-green-3: #298459; --vp-c-green-soft: rgba(16, 185, 129, .16); --vp-c-yellow-1: #f9b44e; --vp-c-yellow-2: #da8b17; --vp-c-yellow-3: #a46a0a; --vp-c-yellow-soft: rgba(234, 179, 8, .16); --vp-c-red-1: #f66f81; --vp-c-red-2: #f14158; --vp-c-red-3: #b62a3c; --vp-c-red-soft: rgba(244, 63, 94, .16) } :root { --vp-c-bg: #ffffff; --vp-c-bg-alt: #f6f6f7; --vp-c-bg-elv: #ffffff; --vp-c-bg-soft: #f6f6f7 } .dark { --vp-c-bg: #1b1b1f; --vp-c-bg-alt: #161618; --vp-c-bg-elv: #202127; --vp-c-bg-soft: #202127 } :root { --vp-c-border: #c2c2c4; --vp-c-divider: #e2e2e3; --vp-c-gutter: #e2e2e3 } .dark { --vp-c-border: #3c3f44; --vp-c-divider: #2e2e32; --vp-c-gutter: #000000 } :root { --vp-c-text-1: rgba(60, 60, 67); --vp-c-text-2: rgba(60, 60, 67, .78); --vp-c-text-3: rgba(60, 60, 67, .56) } :root { --vp-c-default-1: var(--vp-c-gray-1); --vp-c-default-2: var(--vp-c-gray-2); --vp-c-default-3: var(--vp-c-gray-3); --vp-c-default-soft: var(--vp-c-gray-soft); --vp-c-brand-1: var(--vp-c-indigo-1); --vp-c-brand-2: var(--vp-c-indigo-2); --vp-c-brand-3: var(--vp-c-indigo-3); --vp-c-brand-soft: var(--vp-c-indigo-soft); --vp-c-brand: var(--vp-c-brand-1); --vp-c-tip-1: var(--vp-c-brand-1); --vp-c-tip-2: var(--vp-c-brand-2); --vp-c-tip-3: var(--vp-c-brand-3); --vp-c-tip-soft: var(--vp-c-brand-soft); --vp-c-note-1: var(--vp-c-brand-1); --vp-c-note-2: var(--vp-c-brand-2); --vp-c-note-3: var(--vp-c-brand-3); --vp-c-note-soft: var(--vp-c-brand-soft); --vp-c-success-1: var(--vp-c-green-1); --vp-c-success-2: var(--vp-c-green-2); --vp-c-success-3: var(--vp-c-green-3); --vp-c-success-soft: var(--vp-c-green-soft); --vp-c-important-1: var(--vp-c-purple-1); --vp-c-important-2: var(--vp-c-purple-2); --vp-c-important-3: var(--vp-c-purple-3); --vp-c-important-soft: var(--vp-c-purple-soft); --vp-c-warning-1: var(--vp-c-yellow-1); --vp-c-warning-2: var(--vp-c-yellow-2); --vp-c-warning-3: var(--vp-c-yellow-3); --vp-c-warning-soft: var(--vp-c-yellow-soft); --vp-c-danger-1: var(--vp-c-red-1); --vp-c-danger-2: var(--vp-c-red-2); --vp-c-danger-3: var(--vp-c-red-3); --vp-c-danger-soft: var(--vp-c-red-soft); --vp-c-caution-1: var(--vp-c-red-1); --vp-c-caution-2: var(--vp-c-red-2); --vp-c-caution-3: var(--vp-c-red-3); --vp-c-caution-soft: var(--vp-c-red-soft) } .content pre { max-height: initial; } .content pre.shiki, .content pre.shiki code, code[class*=language-], pre:has(code[class*=language-]) { --font-size: 0.8rem; font-size: var(--font-size) !important; line-height: calc(var(--font-size) * 1.5) !important; } pre.shiki { position: relative; z-index: 1; margin: 0; padding: 20px 20px; background: transparent; overflow-x: auto } pre.shiki code { padding: 0; width: fit-content; min-width: 100%; line-height: var(--vp-code-line-height); font-size: var(--vp-code-font-size); color: var(--vp-code-block-color); transition: color .5s } pre.shiki code .highlighted { background-color: var(--vp-code-line-highlight-color); transition: background-color .5s; margin: 0 -24px; padding: 0 24px; width: calc(100% + 44px); display: inline-block } pre.shiki code .highlighted.error { background-color: var(--vp-code-line-error-color) } pre.shiki code .highlighted.warning { background-color: var(--vp-code-line-warning-color) } pre.shiki code .diff { transition: background-color .5s; margin: 0 -24px; padding: 0 24px; width: calc(100% + 44px); display: inline-block } pre.shiki code .diff:before { position: absolute; left: 10px } pre.shiki code .diff.remove { background-color: var(--vp-code-line-diff-remove-color); opacity: .7 } pre.shiki code .diff.remove:before { content: "-"; color: var(--vp-code-line-diff-remove-symbol-color) } pre.shiki code .diff.add { background-color: var(--vp-code-line-diff-add-color) } pre.shiki code .diff.add:before { content: "+"; color: var(--vp-code-line-diff-add-symbol-color) } a[title="Home"] img { border-radius: 100px; } .post-image:not(:has(img)) { display: none !important; } section.post-content details { margin-bottom: 2em; } section.post-content details[open] { margin-bottom: 0; } .kg-toggle-heading-text { padding-block-start: 0 !important; } </style> <script type="comment"> import hljs from 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/es/highlight.min.js'; // and it's easy to individually load additional languages import {setup} from 'https://cdn.jsdelivr.net/npm/highlightjs-glimmer@2.2.1/dist/glimmer.esm.min.js'; setup(hljs); hljs.addPlugin(new LineFocusPlugin({ focusedStyle: { borderLeft: "2px solid #78e08f55", background: "#78e08f05", padding: "2px", paddingLeft: "10px", }, unfocusedStyle: { borderLeft: "2px solid transparent", paddingLeft: "10px", opacity: "0.5", filter: "grayscale(1)" } })); </script> <script type='module'> import { codeToHtml } from 'https://esm.sh/shiki@1.17.0' import {transformerNotationHighlight,transformerNotationDiff} from 'https://esm.sh/@shikijs/transformers@1.17.0' const containers = document.querySelectorAll('code[class*=language-]'); console.log (containers); for (const container of containers) { console.log(container.innerHTML); const className = [...container.classList].find(name => name.startsWith("language-")); const lang = className.split("-").at(-1); const html = await codeToHtml(container.innerText, { lang, theme: 'github-light-high-contrast', transformers: [transformerNotationHighlight(), transformerNotationDiff()] }); container.parentElement.outerHTML = html; } </script> <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/cferdinandi/tabby@12/dist/css/tabby-ui.min.css"> <script src="https://cdn.jsdelivr.net/gh/cferdinandi/tabby@12/dist/js/tabby.polyfills.min.js"></script> </head> <body class="post-template bg-white dark:bg-zinc-950"> <nav class="main-navbar relative h-16 sm:h-20 min-h-max w-full px-4 xl:px-0 !z-40"> <div class="container mx-auto flex h-full max-w-screen-lg items-center justify-between"> <div class="main-navbar-logo flex-[1] justify-start"> <a href="https://yehudakatz.com" class="c-logo-light flex h-fit w-fit items-center"> <img class="c-logo-light-img light-logo min-h-[18px] !max-h-12 w-fit object-contain object-left " style=" height:48px" src="" alt="Katz Got Your Tongue" /> </a> <script> initializeDarkMode(); </script> </div> <div class="main-navbar-links main-nav mx-auto h-full w-fit items-center gap-6 px-4 hidden lg:flex select-none justify-center"> <a href="http://www.yehudakatz.com/" class="main-nav-item nv py-2 text-base lg:py-1 lg:text-md leading-none font-medium text-two hover:opacity-80 transition-opacity duration-200 ">Home</a> <a href="https://yehudakatz.com/about/" class="main-nav-item nv py-2 text-base lg:py-1 lg:text-md leading-none font-medium text-two hover:opacity-80 transition-opacity duration-200 ">About</a> <a href="https://yehudakatz.com/projects/" class="main-nav-item nv py-2 text-base lg:py-1 lg:text-md leading-none font-medium text-two hover:opacity-80 transition-opacity duration-200 ">Projects</a> <a href="https://yehudakatz.com/talks/" class="main-nav-item nv py-2 text-base lg:py-1 lg:text-md leading-none font-medium text-two hover:opacity-80 transition-opacity duration-200 ">Talks</a> <script>colored("nv")</script> </div> <div class="site-login h-full flex w-fit min-w-fit items-center gap-x-3 xs:gap-x-4 flex-[1] justify-end"> <a href="javascript:" data-ghost-search class="main-navbar-search-button primary-button p-0 min-w-[34px] min-h-[34px] w-[34px] h-[34px]" aria-label="Search"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="13" height="13"><path d="M14.5 14.5l-4-4m-4 2a6 6 0 110-12 6 6 0 010 12z" stroke="currentColor"></path></svg> </a> <a data-portal="signin" href="javascript:" aria-label="Sign in" class="main-navbar-sign-button secondary-button text-opacity-80 xs:text-opacity-100 p-0 xs:p-1 xs:px-3 xs:pr-3.5 xs:py-2 min-w-[34px] max-w-[34px] xs:max-w-fit "> <span class="block xs:hidden font-medium"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14"> <path d="M4.5 6.5v-3a3 3 0 016 0V4m-8 2.5h10a1 1 0 011 1v6a1 1 0 01-1 1h-10a1 1 0 01-1-1v-6a1 1 0 011-1z" stroke="currentColor" stroke-width="1.3px"></path> </svg> </span> <span class="main-navbar-sign-button-text hidden xs:block font-medium">✦ Sign in</span> </a> <a href="javascript:" aria-label="mobile-menu" class="main-navbar-mobile-icon primary-button p-0 min-w-[34px] max-w-[34px] min-h-[34px] hamburger lg:hidden"> <svg width="16px" height="16px" stroke-width="1.6" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor"><path d="M3 5H21" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12H21" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 19H21" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"></path></svg> </a> </div> </div> </nav> <div class="mobile-menu mobile-menu-section fixed hidden flex-col top-16 lg:hidden h-[calc(100svh-64px)] w-full bg-white dark:bg-zinc-950 !z-[9999]"> <div class="mobile-menu flex flex-col text-center w-full !overflow-y-auto scroll-two h-[calc(100%-60px)]"> <a href="http://www.yehudakatz.com/" class="mobile-menu-links py-4 text-lg leading-none font-medium text-one focus:bg-zinc-50 dark:focus:bg-zinc-900 border-b border-zinc-50 dark:border-zinc-900/[0.7] last:border-none first:border-t"> Home </a> <a href="https://yehudakatz.com/about/" class="mobile-menu-links py-4 text-lg leading-none font-medium text-one focus:bg-zinc-50 dark:focus:bg-zinc-900 border-b border-zinc-50 dark:border-zinc-900/[0.7] last:border-none first:border-t"> About </a> <a href="https://yehudakatz.com/projects/" class="mobile-menu-links py-4 text-lg leading-none font-medium text-one focus:bg-zinc-50 dark:focus:bg-zinc-900 border-b border-zinc-50 dark:border-zinc-900/[0.7] last:border-none first:border-t"> Projects </a> <a href="https://yehudakatz.com/talks/" class="mobile-menu-links py-4 text-lg leading-none font-medium text-one focus:bg-zinc-50 dark:focus:bg-zinc-900 border-b border-zinc-50 dark:border-zinc-900/[0.7] last:border-none first:border-t"> Talks </a> </div> <div class="mobile-social-accounts flex flex-wrap justify-center items-center w-full h-[60px] bg-zinc-50 dark:bg-zinc-900 border-t border-zinc-100/[0.8] dark:border-zinc-800/[0.7] px-6"> <div class="mobile-social-accounts-items flex flex-wrap flex-row gap-5 items-center justify-center sm:justify-end text-three py-2"> <a href="https://twitter.com/@wycats" target="_blank" rel="noopener" aria-label="Twitter X Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 16 16" width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.52217 6.77491L15.4785 0H14.0671L8.89516 5.88256L4.76437 0H0L6.24656 8.89547L0 16H1.41155L6.87321 9.78782L11.2356 16H16L9.52183 6.77491H9.52217ZM7.58887 8.97384L6.95596 8.08805L1.92015 1.03974H4.0882L8.15216 6.72795L8.78507 7.61374L14.0677 15.0075H11.8997L7.58887 8.97418V8.97384Z" fill="currentColor"/></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Instagram Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11 3.5h1M4.5.5h6a4 4 0 014 4v6a4 4 0 01-4 4h-6a4 4 0 01-4-4v-6a4 4 0 014-4zm3 10a3 3 0 110-6 3 3 0 010 6z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Instagram Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M4.5 6v5m6 0V8.5a2 2 0 10-4 0V11 6M4 4.5h1M1.5.5h12a1 1 0 011 1v12a1 1 0 01-1 1h-12a1 1 0 01-1-1v-12a1 1 0 011-1z" stroke="currentColor"></path></svg> </a> <a href="https://github.com/hedwik" target="_blank" rel="noopener" aria-label="Github Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M5.65 12.477a.5.5 0 10-.3-.954l.3.954zm-3.648-2.96l-.484-.128-.254.968.484.127.254-.968zM9 14.5v.5h1v-.5H9zm.063-4.813l-.054-.497a.5.5 0 00-.299.852l.352-.354zM12.5 5.913h.5V5.91l-.5.002zm-.833-2.007l-.466-.18a.5.5 0 00.112.533l.354-.353zm-.05-2.017l.456-.204a.5.5 0 00-.319-.276l-.137.48zm-2.173.792l-.126.484a.5.5 0 00.398-.064l-.272-.42zm-3.888 0l-.272.42a.5.5 0 00.398.064l-.126-.484zM3.383 1.89l-.137-.48a.5.5 0 00-.32.276l.457.204zm-.05 2.017l.354.353a.5.5 0 00.112-.534l-.466.181zM2.5 5.93H3v-.002l-.5.002zm3.438 3.758l.352.355a.5.5 0 00-.293-.851l-.06.496zM5.5 11H6l-.001-.037L5.5 11zM5 14.5v.5h1v-.5H5zm.35-2.977c-.603.19-.986.169-1.24.085-.251-.083-.444-.25-.629-.49a4.8 4.8 0 01-.27-.402c-.085-.139-.182-.302-.28-.447-.191-.281-.473-.633-.929-.753l-.254.968c.08.02.184.095.355.346.082.122.16.252.258.412.094.152.202.32.327.484.253.33.598.663 1.11.832.51.168 1.116.15 1.852-.081l-.3-.954zm4.65-.585c0-.318-.014-.608-.104-.878-.096-.288-.262-.51-.481-.727l-.705.71c.155.153.208.245.237.333.035.105.053.254.053.562h1zm-.884-.753c.903-.097 1.888-.325 2.647-.982.78-.675 1.237-1.729 1.237-3.29h-1c0 1.359-.39 2.1-.892 2.534-.524.454-1.258.653-2.099.743l.107.995zM13 5.91a3.354 3.354 0 00-.98-2.358l-.707.706c.438.44.685 1.034.687 1.655l1-.003zm-.867-1.824c.15-.384.22-.794.21-1.207l-1 .025a2.12 2.12 0 01-.142.82l.932.362zm.21-1.207a3.119 3.119 0 00-.27-1.195l-.913.408c.115.256.177.532.184.812l1-.025zm-.726-.99c.137-.481.137-.482.136-.482h-.003l-.004-.002a.462.462 0 00-.03-.007 1.261 1.261 0 00-.212-.024 2.172 2.172 0 00-.51.054c-.425.091-1.024.317-1.82.832l.542.84c.719-.464 1.206-.634 1.488-.694a1.2 1.2 0 01.306-.03l-.008-.001a.278.278 0 01-.01-.002l-.006-.002h-.003l-.002-.001c-.001 0-.002 0 .136-.482zm-2.047.307a8.209 8.209 0 00-4.14 0l.252.968a7.209 7.209 0 013.636 0l.252-.968zm-3.743.064c-.797-.514-1.397-.74-1.822-.83a2.17 2.17 0 00-.51-.053 1.259 1.259 0 00-.241.03l-.004.002h-.003l.136.481.137.481h-.001l-.002.001-.003.001a.327.327 0 01-.016.004l-.008.001h.008a1.19 1.19 0 01.298.03c.282.06.769.23 1.488.694l.543-.84zm-2.9-.576a3.12 3.12 0 00-.27 1.195l1 .025a2.09 2.09 0 01.183-.812l-.913-.408zm-.27 1.195c-.01.413.06.823.21 1.207l.932-.362a2.12 2.12 0 01-.143-.82l-1-.025zm.322.673a3.354 3.354 0 00-.726 1.091l.924.38c.118-.285.292-.545.51-.765l-.708-.706zm-.726 1.091A3.354 3.354 0 002 5.93l1-.003c0-.31.06-.616.177-.902l-.924-.38zM2 5.93c0 1.553.458 2.597 1.239 3.268.757.65 1.74.88 2.64.987l.118-.993C5.15 9.09 4.416 8.89 3.89 8.438 3.388 8.007 3 7.276 3 5.928H2zm3.585 3.404c-.5.498-.629 1.09-.584 1.704L6 10.963c-.03-.408.052-.683.291-.921l-.705-.709zM5 11v3.5h1V11H5zm5 3.5V13H9v1.5h1zm0-1.5v-2.063H9V13h1z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M.5.5V0a.5.5 0 00-.5.5h.5zm14 0h.5a.5.5 0 00-.5-.5v.5zm0 8l.354.354A.5.5 0 0015 8.5h-.5zm-3 3v.5a.5.5 0 00.354-.146L11.5 11.5zm-5 0V11a.5.5 0 00-.325.12l.325.38zm-3.5 3h-.5a.5.5 0 00.825.38L3 14.5zm0-3h.5A.5.5 0 003 11v.5zm-2.5 0H0a.5.5 0 00.5.5v-.5zM.5 1h14V0H.5v1zM14 .5v8h1v-8h-1zm.146 7.646l-3 3 .708.708 3-3-.708-.708zM11.5 11h-5v1h5v-1zm-5.325.12l-3.5 3 .65.76 3.5-3-.65-.76zM3.5 14.5v-3h-1v3h1zM3 11H.5v1H3v-1zm-2 .5V.5H0v11h1zM10 3v5h1V3h-1zM7 3v5h1V3H7z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M9.5 0v11A3.5 3.5 0 116 7.5m8-2A4.5 4.5 0 019.5 1" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M1.61 12.738l-.104.489.105-.489zm11.78 0l.104.489-.105-.489zm0-10.476l.104-.489-.105.489zm-11.78 0l.106.489-.105-.489zM6.5 5.5l.277-.416A.5.5 0 006 5.5h.5zm0 4H6a.5.5 0 00.777.416L6.5 9.5zm3-2l.277.416a.5.5 0 000-.832L9.5 7.5zM0 3.636v7.728h1V3.636H0zm15 7.728V3.636h-1v7.728h1zM1.506 13.227c3.951.847 8.037.847 11.988 0l-.21-.978a27.605 27.605 0 01-11.568 0l-.21.978zM13.494 1.773a28.606 28.606 0 00-11.988 0l.21.978a27.607 27.607 0 0111.568 0l.21-.978zM15 3.636c0-.898-.628-1.675-1.506-1.863l-.21.978c.418.09.716.458.716.885h1zm-1 7.728a.905.905 0 01-.716.885l.21.978A1.905 1.905 0 0015 11.364h-1zm-14 0c0 .898.628 1.675 1.506 1.863l.21-.978A.905.905 0 011 11.364H0zm1-7.728c0-.427.298-.796.716-.885l-.21-.978A1.905 1.905 0 000 3.636h1zM6 5.5v4h1v-4H6zm.777 4.416l3-2-.554-.832-3 2 .554.832zm3-2.832l-3-2-.554.832 3 2 .554-.832z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7.5 1c-1.155 0-2.174.412-2.894 1.281-.642.775-1.006 2.35-1.066 3.666l-.073.01-.022.004a8.68 8.68 0 00-.368.059c-.465.089-1.346.326-1.543 1.277-.093.445.011.833.247 1.134.211.269.497.429.708.53.09.041.181.08.274.117-.21.584-.579 1.184-.987 1.728-.382.508-.28 1.153-.083 1.573.197.421.57.402 1.192.43.352.015.722.051 1.09.12.166.03.362.098.606.2.142.06.283.123.423.187.113.052.235.106.374.167.573.25 1.276.517 2.056.517s1.483-.267 2.055-.517c.14-.06.26-.115.375-.167l.025-.012c.135-.06.26-.117.398-.174.243-.103.44-.17.606-.201a7.951 7.951 0 011.09-.12c.622-.028 1.104-.009 1.303-.43.197-.42.298-1.065-.084-1.573-.406-.54-.772-1.136-.983-1.716a5.24 5.24 0 00.305-.127c.216-.098.518-.261.73-.543.245-.326.315-.739.175-1.184-.28-.886-1.092-1.122-1.568-1.216a6.857 6.857 0 00-.355-.058l-.012-.002-.056-.009c-.065-1.234-.41-2.795-1.036-3.581C9.695 1.485 8.682 1 7.5 1z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M4.5 13.5l3-7m-3.236 3a2.989 2.989 0 01-.764-2V7A3.5 3.5 0 017 3.5h1A3.5 3.5 0 0111.5 7v.5a3 3 0 01-3 3 2.081 2.081 0 01-1.974-1.423L6.5 9m1 5.5a7 7 0 110-14 7 7 0 010 14z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7.5 1.5l.121-.485A.5.5 0 007 1.5h.5zm5.5 8c0 .774-.55 1.641-1.583 2.343C10.4 12.533 8.998 13 7.5 13v1c1.696 0 3.294-.525 4.479-1.33C13.148 11.876 14 10.743 14 9.5h-1zM7.5 13c-1.498 0-2.9-.466-3.917-1.157C2.551 11.14 2 10.273 2 9.5H1c0 1.243.852 2.376 2.021 3.17C4.206 13.475 5.804 14 7.5 14v-1zM2 9.5c0-.774.55-1.641 1.583-2.343C4.6 6.467 6.002 6 7.5 6V5c-1.696 0-3.294.525-4.479 1.33C1.852 7.124 1 8.257 1 9.5h1zM7.5 6c1.498 0 2.9.467 3.917 1.157C12.449 7.86 13 8.727 13 9.5h1c0-1.243-.852-2.376-2.021-3.17C10.794 5.525 9.196 5 7.5 5v1zm2.306 4.54c-.69.29-1.32.46-2.306.46v1c1.136 0 1.898-.204 2.694-.54l-.388-.92zM7.5 11c-.987 0-1.617-.17-2.306-.46l-.388.92c.796.336 1.558.54 2.694.54v-1zM8 5.5v-4H7v4h1zm-.621-3.515l4 1 .242-.97-4-1-.242.97zM3.974 6.841c-.286-.855-1.12-1.297-1.952-1.297v1c.51 0 .886.261 1.004.615l.948-.318zM2.022 5.544A2.022 2.022 0 000 7.566h1a1.02 1.02 0 011.022-1.022v-1zM0 7.566C0 8.589.76 9.424 1.74 9.56l.139-.99A1.016 1.016 0 011 7.565H0zm11.974-.407c.118-.354.493-.615 1.004-.615v-1c-.832 0-1.666.442-1.952 1.297l.948.318zm1.004-.615A1.02 1.02 0 0114 7.566h1a2.022 2.022 0 00-2.022-2.022v1zM14 7.566c0 .511-.38.934-.879 1.004l.139.99A2.016 2.016 0 0015 7.567h-1zM12.5 3a.5.5 0 01-.5-.5h-1A1.5 1.5 0 0012.5 4V3zm.5-.5a.5.5 0 01-.5.5v1A1.5 1.5 0 0014 2.5h-1zm-.5-.5a.5.5 0 01.5.5h1A1.5 1.5 0 0012.5 1v1zm0-1A1.5 1.5 0 0011 2.5h1a.5.5 0 01.5-.5V1zM5 9h1V8H5v1zm4 0h1V8H9v1z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="mobile-social-accounts-item footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11.5 13.5l-.326.379a.5.5 0 00.342.12L11.5 13.5zm-1.066-1.712a.5.5 0 00-.785.62l.785-.62zm.398-.41l-.174-.468a.672.672 0 00-.02.007l.194.461zm-1.738.516L9.01 11.4l-.008.001.092.492zm-3.104-.012l-.095.49.003.001.092-.491zm-1.762-.515l-.182.465.182-.466zm-.875-.408l-.278.415a.46.46 0 00.033.021l.245-.436zm-.108-.06l.277-.416a.491.491 0 00-.054-.031l-.223.447zm-.048-.036l.353-.354a.502.502 0 00-.11-.083l-.243.437zm2.154 1.52a.5.5 0 00-.785-.62l.785.62zM3.5 13.5l-.016.5a.5.5 0 00.347-.125L3.5 13.5zm-3-2.253H0a.5.5 0 00.006.08l.494-.08zm1.726-8.488l-.3-.4a.5.5 0 00-.168.225l.468.175zM5.594 1.5l.498-.047A.5.5 0 005.605 1l-.01.5zm-.378 1.306a.5.5 0 00.996-.095l-.996.095zm3.526-.063a.5.5 0 00.992.127l-.992-.127zM9.406 1.5L9.395 1a.5.5 0 00-.485.436l.496.064zm3.368 1.259l.468-.175a.5.5 0 00-.168-.225l-.3.4zm1.726 8.488l.494.08a.497.497 0 00.006-.08h-.5zM6.481 8.8l-.5-.008V8.8h.5zm5.019 4.7l.326-.379-.002-.002a.794.794 0 01-.044-.038 21.355 21.355 0 01-.536-.48c-.325-.298-.66-.622-.81-.813l-.785.62c.208.264.603.64.918.93a29.109 29.109 0 00.593.53l.01.008.003.002.327-.378zm.436-3.246c-.46.303-.894.513-1.278.656l.348.937a7.352 7.352 0 001.48-.758l-.55-.835zm-1.297.663a7.387 7.387 0 01-1.629.484l.168.986a8.39 8.39 0 001.848-.548l-.387-.922zm-1.637.485a7.895 7.895 0 01-2.92-.012l-.184.983a8.896 8.896 0 003.288.012l-.184-.983zm-2.917-.011a9.57 9.57 0 01-1.675-.49l-.364.931c.512.2 1.13.402 1.849.54l.19-.981zm-1.675-.49a6.536 6.536 0 01-.813-.378l-.489.872c.326.183.648.324.938.437l.364-.931zm-.78-.358a.802.802 0 00-.108-.061c-.02-.01-.011-.007 0 .001l-.555.832a.87.87 0 00.108.061c.021.01.012.007 0-.002l.556-.83zm-.162-.091a.332.332 0 01.082.058l-.707.707c.023.023.081.08.178.13l.447-.895zm-.028-.026a4.697 4.697 0 01-.28-.168l-.011-.008a.025.025 0 00-.001 0l-.287.41-.286.409.001.001.002.002.007.004.021.014.075.049c.064.04.156.096.273.161l.486-.874zm1.126 1.338c-.152.193-.489.525-.813.829a30.38 30.38 0 01-.538.491l-.034.031-.01.008-.001.002h-.001l.331.375.331.375.001-.001.003-.002.01-.009.036-.032a38.039 38.039 0 00.555-.508c.315-.296.708-.677.915-.94l-.785-.62zM3.516 13c-1.166-.037-1.778-.521-2.11-.96a2.394 2.394 0 01-.4-.82 1.1 1.1 0 01-.013-.056v.002l-.493.08c-.494.08-.494.08-.493.081v.006a1.367 1.367 0 00.028.127 3.394 3.394 0 00.573 1.183c.505.667 1.393 1.31 2.876 1.357l.032-1zM1 11.247c0-1.867.42-3.94.847-5.564a35.45 35.45 0 01.776-2.552 16.43 16.43 0 01.067-.186l.004-.01v-.001l-.468-.175-.469-.175v.001l-.001.003-.004.011a9.393 9.393 0 00-.072.2 36.445 36.445 0 00-.8 2.629C.443 7.083 0 9.253 0 11.247h1zm1.526-8.088c.8-.6 1.577-.89 2.15-1.03a4.764 4.764 0 01.86-.128A1.48 1.48 0 015.585 2h-.001l.01-.5.01-.5H5.6a.848.848 0 00-.028 0h-.068a3.973 3.973 0 00-.24.016 5.763 5.763 0 00-.825.141 6.938 6.938 0 00-2.513 1.2l.6.8zm2.57-1.612l.12 1.259.996-.095-.12-1.258-.996.094zM9.734 2.87l.168-1.306-.992-.128-.168 1.307.992.127zM9.406 1.5l.01.5h-.001a.497.497 0 01.049 0c.038.002.1.005.179.013.16.014.394.047.681.117a5.94 5.94 0 012.15 1.029l.6-.8a6.937 6.937 0 00-2.513-1.2 5.76 5.76 0 00-.825-.142A3.98 3.98 0 009.399 1h-.003l.01.5zm3.368 1.259l-.469.174.001.003.004.009.013.037.053.149a35.482 35.482 0 01.777 2.552c.428 1.624.847 3.697.847 5.564h1c0-1.994-.444-4.164-.88-5.819a36.512 36.512 0 00-.8-2.629 15.246 15.246 0 00-.057-.158l-.015-.042-.004-.01-.001-.004-.47.174zm1.726 8.488l-.493-.08v-.003l-.002.008-.01.047c-.012.045-.03.113-.061.197-.062.17-.167.396-.34.624-.332.439-.944.923-2.11.96l.032 1c1.483-.047 2.37-.69 2.876-1.356a3.395 3.395 0 00.573-1.184 2.05 2.05 0 00.026-.118l.002-.01v-.004c0-.001 0-.002-.493-.081zM5.259 6.97c-1.002 0-1.723.867-1.723 1.83h1c0-.498.357-.83.723-.83v-1zM3.536 8.8c0 .967.736 1.83 1.723 1.83v-1c-.357 0-.723-.334-.723-.83h-1zm1.723 1.83c1 0 1.722-.866 1.722-1.83h-1c0 .5-.357.83-.722.83v1zM6.98 8.81c.016-.978-.728-1.84-1.722-1.84v1.001c.372 0 .73.338.722.822l1 .017zm2.653-1.84c-1.002.001-1.723.868-1.723 1.831h1c0-.498.357-.83.723-.83v-1zM7.91 8.802c0 .967.736 1.83 1.723 1.83v-1c-.357 0-.723-.334-.723-.83h-1zm1.723 1.83c1 0 1.722-.866 1.722-1.83h-1c0 .5-.357.83-.722.83v1zm1.722-1.83c0-.963-.721-1.83-1.722-1.83v1c.365 0 .722.332.722.83h1zM3.74 4.44c1.443-.787 2.619-1.154 3.763-1.155 1.145 0 2.318.365 3.758 1.154l.48-.876c-1.522-.835-2.865-1.279-4.238-1.278-1.373 0-2.717.445-4.241 1.277l.478.878z" fill="currentColor"></path></svg> </a> </div> </div> </div> <nav class="post-nav flex items-center justify-center -translate-y-[61px] sm:-translate-y-[71px] h-16 sm:h-[74px] transition-transform duration-300 ease-in-out fixed top-0 w-full bg-white dark:bg-zinc-900 !z-[999999] px-4 sm:px-6"> <div class="post-nav-content w-full flex justify-between items-center max-w-screen-lg gap-3"> <div class="post-nav-left flex items-center justify-start gap-3 xs:gap-4"> <h4 class="post-nav-title text-one font-semibold text-base sm:text-lg line-clamp-1">Generic Actions in Rails 3</h4> </div> <button type="button" class="post-nav-button share-button size-9 min-w-9 max-w-9 sm:px-4 sm:h-9 sm:w-fit sm:max-w-fit flex items-center justify-center gap-2 text-two border border-zinc-200/70 bg-zinc-100 dark:bg-zinc-800 dark:border-zinc-700/70 hover:opacity-80 transition-opacity duration-300 rounded-full"> <svg class="copying" width="17px" height="17px" stroke-width="2" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor"><path d="M19.4 20H9.6C9.26863 20 9 19.7314 9 19.4V9.6C9 9.26863 9.26863 9 9.6 9H19.4C19.7314 9 20 9.26863 20 9.6V19.4C20 19.7314 19.7314 20 19.4 20Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 9V4.6C15 4.26863 14.7314 4 14.4 4H4.6C4.26863 4 4 4.26863 4 4.6V14.4C4 14.7314 4.26863 15 4.6 15H9" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg> <svg class="copied hidden" width="17px" height="17px" stroke-width="2" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="currentColor"><path d="M5 13L9 17L19 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg> <span class="post-nav-button-text text-current font-medium text-sm hidden sm:block">Copy Link</span> </button> </div> <div class="scroll-line-content absolute bottom-0 left-0 w-full h-[3px] bg-zinc-200/30 dark:bg-zinc-200/5"> <div class="scroll-line fixed h-[3px] rounded-r-sm bg-ghost-accent bottom-0 before:absolute before:content-[''] before:inset-0 before:size-full before:opacity-30 before:shadow-[0px_0.5px_4px_var(--ghost-accent-color)]"></div> </div> </nav> <style> .scroll-line { transition: 0.5s cubic-bezier(0.075, 0.82, 0.165, 1) width; z-index: 1; } </style> <div class="native-post-content xs:my-16 mx-auto my-12 flex w-full flex-col items-center justify-start gap-2 px-4 sm:my-24 md:max-w-2xl lg:px-0"> <div class="post-heading post-heading-centered xs:gap-6 flex w-full flex-col items-start justify-start gap-4 sm:items-center md:gap-8"> <div class="post-info text-four flex flex-row items-center justify-start gap-3 sm:justify-center"> <span class="post-heading-centered-date text-md text-left font-normal sm:text-center">20 Dec 2009</span> <i class="separator cursor-default text-[11px] !text-zinc-400 dark:!text-zinc-600">•</i> <span class="post-heading-centered-reading-time text-md text-left font-normal sm:text-center">4 min read</span> </div> <h1 class="post-title xxs:text-3xl text-one w-full text-left text-[28px] font-bold !leading-[1.2] sm:text-center sm:text-4xl md:text-5xl">Generic Actions in Rails 3</h1> <div class="post-authors hidden sm:block"> <div class="c-author-area xs:flex -ml-1 hidden select-none flex-row items-center"> <a href="/author/wycats/" aria-label="Yehuda Katz" class="c-author-area-item flex flex-row rounded-full gap-0 first:order-1 first:mr-1 [&:nth-child(3)]:order-2 [&:nth-child(3)]:z-10 [&:nth-child(3)]:-ml-5 [&:nth-child(3)]:mr-1.5 "> <figure class="c-author-area-image aspect-[1/1] overflow-hidden rounded-full border-white bg-zinc-100 hover:z-20 dark:border-zinc-950 dark:bg-zinc-900 h-10 w-10 border-4 "> <img class="c-author-area-image-img lazyhedwik-image h-full w-full rounded-full object-cover object-center" src="/content/images/size/w90/format/webp/2021/04/Profile---Correct.png" data-src="/content/images/size/w180/format/webp/2021/04/Profile---Correct.png" alt="Yehuda Katz" /> </figure> </a> <a href="/author/wycats/" aria-label="Yehuda Katz" class="c-author-area-button author-1 wycats [&:nth-child(2)]:order-3 [&:nth-child(4)]:order-4 [&:nth-child(4)]:before:inline-block [&:nth-child(4)]:before:content-['&'] [&:nth-child(4)]:before:!no-underline [&:nth-child(4)]:before:hover:text-two [&:nth-child(4)]:before:mr-1.5 [&:nth-child(4)]:ml-1.5 text-base [&:nth-child(4)]:before:opacity-50 text-two hover:underline before:decoration-transparent underline-offset-6 decoration-dotted decoration-transparent hover:text-ghost-accent hover:decoration-ghost-accent font-medium transition-colors duration-300">Yehuda Katz</a> </div> </div> <div class="post-image aspect-[12/7] h-fit w-full select-none overflow-hidden bg-zinc-50 sm:rounded-2xl dark:bg-zinc-900"> </div> </div> <div class="content"> <!--kg-card-begin: markdown--><p>So Django has an interesting feature called "generic views", which essentially allow you to to render a template with generic code. In Rails, the same feature would be called "generic actions" (just a terminology difference).</p> <p>This was possible, but somewhat difficult in Rails 2.x, but it's a breeze in Rails 3.</p> <p>Let's take a look at a simple generic view in Django, the "redirect_to" view:</p> <pre><code class="language-python">urlpatterns = patterns('django.views.generic.simple', ('^foo/(?P<id>\d+)/$', 'redirect_to', {'url': '/bar/%(id)s/'}), ) </code></pre> <p>This essentially redirects <code>"/foo/<id>"</code> to <code>"/bar/<id>s/"</code>. In Rails 2.3, a way to achieve equivalent behavior was to create a generic controller that handled this:</p> <pre><code class="language-ruby">class GenericController < ApplicationController def redirect redirect_to(params[:url] % params, params[:options]) end end </code></pre> <p>And then you could use this in your router:</p> <pre><code class="language-ruby">map.connect "/foo/:id", :controller => "generic", :action => "redirect", :url => "/bar/%{id}s" </code></pre> <p>This uses the new Ruby 1.9 interpolation syntax ("%{first} %{last}" % {:foo => "hello", :bar => "sir"} == "hello sir") that has been backported to Ruby 1.8 via ActiveSupport.</p> <h2>Better With Rails 3</h2> <p>However, this is a bit clumsy, and requires us to have a special controller to handle this (relatively simple) case. It also saddles us with the conceptual overhead of a controller in the router itself.</p> <p>Here's how you do the same thing in Rails 3:</p> <pre><code class="language-ruby">match "/foo/:id", :to => redirect("/bar/%{id}s") </code></pre> <p>This is built-into Rails 3's router, but the way it works is actually pretty cool. The Rails 3 router is conceptually decoupled from Rails itself, and the <code>:to</code> key points at a Rack endpoint. For instance, the following would be a valid route in Rails 3:</p> <pre><code class="language-ruby">match "/foo", :to => proc {|env| [200, {}, ["Hello world"]] } </code></pre> <p>The <code>redirect</code> method simply returns a rack endpoint that knows how to handle the redirection:</p> <pre><code class="language-ruby">def redirect(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} path = args.shift || block path_proc = path.is_a?(Proc) ? path : proc {|params| path % params } status = options[:status] || 301 lambda do |env| req = Rack::Request.new(env) params = path_proc.call(env["action_dispatch.request.path_parameters"]) url = req.scheme + '://' + req.host + params [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] end end </code></pre> <p>There's a few things going on here, but the important part is the last few lines, where the <code>redirect</code> method returns a valid Rack endpoint. If you look closely at the code, you can see that the following would be valid as well:</p> <pre><code class="language-ruby">match "/api/v1/:api", :to => redirect {|params| "/api/v2/#{params[:api].pluralize}" } # and match "/api/v1/:api", :to => redirect(:status => 302) {|params| "/api/v2/#{params[:api].pluralize}" } </code></pre> <h2>Another Generic Action</h2> <p>Another nice generic action that Django provides is allowing you to render a template directly without needing an explicit action. It looks like this:</p> <pre><code class="language-python">urlpatterns = patterns('django.views.generic.simple', (r'^foo/$', 'direct_to_template', {'template': 'foo_index.html'}), (r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template': 'foo_detail.html'}), ) </code></pre> <p>This provides a special mechanism for rendering a template directly from the Django router. Again, this could be implemented by creating a special controller in Rails 2 and used as follows:</p> <pre><code class="language-ruby">class GenericController < ApplicationController def direct_to_template render(params[:options]) end end # Router map.connect "/foo", :controller => "generic", :action => "direct_to_template", :options => {:template => "foo_detail"} </code></pre> <h2>A Prettier API</h2> <p>A nicer way to do this would be something like this:</p> <pre><code class="language-ruby">match "/foo", :to => render("foo") </code></pre> <p>For the sake of clarity, let's say that directly rendered templates will come out of <code>app/views/direct</code> unless otherwise specified. Also, let's say that the render method should work identically to the render method used in Rails controllers themselves, so that <code lang="ruby">render :template => "foo", :status => 201, :content_type => Mime::JSON</code> et al will work as expected.</p> <p>In order to make this work, we'll use <code>ActionController::Metal</code>, which exposes a Rack-compatible object with access to all of the powers of a full <code>ActionController::Base</code> object.</p> <pre><code class="language-ruby">class RenderDirectly < ActionController::Metal include ActionController::Rendering include ActionController::Layouts append_view_path Rails.root.join("app", "views", "direct") append_view_path Rails.root.join("app", "views") layout "application" def index render *env["generic_views.render_args"] end end module GenericActions module Render def render(*args) app = RenderDirectly.action(:index) lambda do |env| env["generic_views.render_args"] = args app.call(env) end end end end </code></pre> <p>The trick here is that we're subclassing <code>ActionController::Metal</code> and pulling in just <code>Rendering</code> and <code>Layouts</code>, which gives you full access to the normal rendering API without any of the other overhead of normal controllers. We add both the <code>direct</code> directory and the normal view directory to the view path, which means that any templates you place inside <code>app/views/direct</code> will take be used first, but it'll fall back to the normal view directory for layouts or partials. We also specify that the layout is <code>application</code>, which is not the default in Rails 3 <strong>in this case</strong> since our metal controller does not inherit from <code>ApplicationController</code>.</p> <h3>Note for the Curious</h3> <blockquote>In all normal application cases, Rails will look up the inheritance chain for a named layout matching the controller name. This means that the Rails 2 behavior, which allows you to provide a layout named after the controller, still works exactly the same as before, and that <code>ApplicationController</code> is just another controller name, and <code>application.html.erb</code> is its default layout.</blockquote> <p>And then, the actual use in your application:</p> <pre><code class="language-ruby">Rails.application.routes do extend GenericActions match "/foo", :to => render("foo_index") # match "/foo" => render("foo_index") is a valid shortcut for the simple case match "/foo/:id", :constraints => {:id => /\d+/}, :to => render("foo_detail") end </code></pre> <p>Of course, because we're using a real controller shell, you'll be able to use any other options available on the render (like :status, :content_type, :location, :action, :layout, etc.).</p> <!--kg-card-end: markdown--> </div> <div class="c-post-footer post-footer flex flex-col gap-9 items-start justify-start w-full"> <div class="c-post-footer-unless-member lazyhedwik-element relative flex flex-col sm:flex-row items-start sm:items-center py-6 px-7 gap-4 justify-between w-full h-fit bg-transparent after:absolute after:z-[-2] after:inset-0 after:w-full after:h-full after:opacity-[0.06] after:border-[1.6px] after:border-ghost-accent after:rounded-2xl before:absolute before:z-[-1] before:inset-0 before:w-full before:h-full before:bg-ghost-accent before:rounded-2xl before:opacity-[0.036] dark:before:bg-zinc-900 dark:before:opacity-70 dark:after:border-zinc-800 dark:after:opacity-80 rounded-xl select-none"> <div class="c-post-footer-unless-member-content flex w-full flex-col xs:flex-row items-center gap-3 xxs:gap-4 xs:gap-5 justify-center xs:justify-start"> <div class="cta-favicon"> <img src="https://yehudakatz.com/content/images/2024/08/yehuda-square.png" alt="Katz Got Your Tongue" class="aspect-[1/1] min-w-[64px] min-h-[64px] w-16 h-16"> </div> <div class="c-post-footer-texts flex flex-col items-center justify-start xs:items-start xs:justify-start gap-2"> <h5 class="c-post-footer-head text-lg font-bold leading-none text-one text-center xs:text-left line-clamp-1">Enjoy this Post?</h5> <span class="c-post-footer-description text-sm leading-subheading text-three text-center xs:text-left sm:line-clamp-1">Dive into the unlimited – upgrade to premium now!</span> </div> </div> <a href="javascript:" data-portal="signup" class="c-post-footer-button secondary-button w-full sm:max-w-[120px] h-10 min-h-10 max-h-10 font-medium rounded-full">✦ Sign up</a> </div> <hr class="c-post-footer-divider border-t border-zinc-100/[0.8] dark:border-zinc-800/[0.8] w-full"> <div class="related-posts-section lazyhedwik-element flex w-full flex-col items-start justify-start gap-4"> <h3 class="related-posts-head text-three text-[13px] font-semibold uppercase tracking-wider">You might also like</h3> </div></div></div> <footer class="footer footer-primary lazyhedwik-element footer-minimal xs:px-6 xs:py-12 relative border-t !border-zinc-100/[0.7] dark:!border-zinc-900 flex h-fit w-full select-none flex-col items-center justify-center px-4 lg:px-0 py-10 sm:py-14"> <div class="footer-primary-content flex flex-col items-center justify-center gap-6"> <a href="https://yehudakatz.com" class="c-footer-light-logo flex h-fit w-fit items-center footer-logo"> <img class="c-footer-logo-img footer-light-logo !w-fit object-contain min-h-[24px] object-center" style=" height:36px" src="" alt="Katz Got Your Tongue" /> </a> <script> initializeDarkMode(); </script> <span class="footer-primary-description text-two max-w-md text-center text-lg font-medium leading-normal">Long-form writing by Yehuda Katz, co-creator of Ember.js and serial Open Sourcerer.</span> <div class="footer-social-accounts footer-primary-social-accounts flex flex-row gap-5 items-center justify-start sm:justify-end text-three py-2"> <a href="https://twitter.com/@wycats" target="_blank" rel="noopener" aria-label="Twitter X Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 16 16" width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9.52217 6.77491L15.4785 0H14.0671L8.89516 5.88256L4.76437 0H0L6.24656 8.89547L0 16H1.41155L6.87321 9.78782L11.2356 16H16L9.52183 6.77491H9.52217ZM7.58887 8.97384L6.95596 8.08805L1.92015 1.03974H4.0882L8.15216 6.72795L8.78507 7.61374L14.0677 15.0075H11.8997L7.58887 8.97418V8.97384Z" fill="currentColor"/></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Instagram Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11 3.5h1M4.5.5h6a4 4 0 014 4v6a4 4 0 01-4 4h-6a4 4 0 01-4-4v-6a4 4 0 014-4zm3 10a3 3 0 110-6 3 3 0 010 6z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Instagram Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M4.5 6v5m6 0V8.5a2 2 0 10-4 0V11 6M4 4.5h1M1.5.5h12a1 1 0 011 1v12a1 1 0 01-1 1h-12a1 1 0 01-1-1v-12a1 1 0 011-1z" stroke="currentColor"></path></svg> </a> <a href="https://github.com/hedwik" target="_blank" rel="noopener" aria-label="Github Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M5.65 12.477a.5.5 0 10-.3-.954l.3.954zm-3.648-2.96l-.484-.128-.254.968.484.127.254-.968zM9 14.5v.5h1v-.5H9zm.063-4.813l-.054-.497a.5.5 0 00-.299.852l.352-.354zM12.5 5.913h.5V5.91l-.5.002zm-.833-2.007l-.466-.18a.5.5 0 00.112.533l.354-.353zm-.05-2.017l.456-.204a.5.5 0 00-.319-.276l-.137.48zm-2.173.792l-.126.484a.5.5 0 00.398-.064l-.272-.42zm-3.888 0l-.272.42a.5.5 0 00.398.064l-.126-.484zM3.383 1.89l-.137-.48a.5.5 0 00-.32.276l.457.204zm-.05 2.017l.354.353a.5.5 0 00.112-.534l-.466.181zM2.5 5.93H3v-.002l-.5.002zm3.438 3.758l.352.355a.5.5 0 00-.293-.851l-.06.496zM5.5 11H6l-.001-.037L5.5 11zM5 14.5v.5h1v-.5H5zm.35-2.977c-.603.19-.986.169-1.24.085-.251-.083-.444-.25-.629-.49a4.8 4.8 0 01-.27-.402c-.085-.139-.182-.302-.28-.447-.191-.281-.473-.633-.929-.753l-.254.968c.08.02.184.095.355.346.082.122.16.252.258.412.094.152.202.32.327.484.253.33.598.663 1.11.832.51.168 1.116.15 1.852-.081l-.3-.954zm4.65-.585c0-.318-.014-.608-.104-.878-.096-.288-.262-.51-.481-.727l-.705.71c.155.153.208.245.237.333.035.105.053.254.053.562h1zm-.884-.753c.903-.097 1.888-.325 2.647-.982.78-.675 1.237-1.729 1.237-3.29h-1c0 1.359-.39 2.1-.892 2.534-.524.454-1.258.653-2.099.743l.107.995zM13 5.91a3.354 3.354 0 00-.98-2.358l-.707.706c.438.44.685 1.034.687 1.655l1-.003zm-.867-1.824c.15-.384.22-.794.21-1.207l-1 .025a2.12 2.12 0 01-.142.82l.932.362zm.21-1.207a3.119 3.119 0 00-.27-1.195l-.913.408c.115.256.177.532.184.812l1-.025zm-.726-.99c.137-.481.137-.482.136-.482h-.003l-.004-.002a.462.462 0 00-.03-.007 1.261 1.261 0 00-.212-.024 2.172 2.172 0 00-.51.054c-.425.091-1.024.317-1.82.832l.542.84c.719-.464 1.206-.634 1.488-.694a1.2 1.2 0 01.306-.03l-.008-.001a.278.278 0 01-.01-.002l-.006-.002h-.003l-.002-.001c-.001 0-.002 0 .136-.482zm-2.047.307a8.209 8.209 0 00-4.14 0l.252.968a7.209 7.209 0 013.636 0l.252-.968zm-3.743.064c-.797-.514-1.397-.74-1.822-.83a2.17 2.17 0 00-.51-.053 1.259 1.259 0 00-.241.03l-.004.002h-.003l.136.481.137.481h-.001l-.002.001-.003.001a.327.327 0 01-.016.004l-.008.001h.008a1.19 1.19 0 01.298.03c.282.06.769.23 1.488.694l.543-.84zm-2.9-.576a3.12 3.12 0 00-.27 1.195l1 .025a2.09 2.09 0 01.183-.812l-.913-.408zm-.27 1.195c-.01.413.06.823.21 1.207l.932-.362a2.12 2.12 0 01-.143-.82l-1-.025zm.322.673a3.354 3.354 0 00-.726 1.091l.924.38c.118-.285.292-.545.51-.765l-.708-.706zm-.726 1.091A3.354 3.354 0 002 5.93l1-.003c0-.31.06-.616.177-.902l-.924-.38zM2 5.93c0 1.553.458 2.597 1.239 3.268.757.65 1.74.88 2.64.987l.118-.993C5.15 9.09 4.416 8.89 3.89 8.438 3.388 8.007 3 7.276 3 5.928H2zm3.585 3.404c-.5.498-.629 1.09-.584 1.704L6 10.963c-.03-.408.052-.683.291-.921l-.705-.709zM5 11v3.5h1V11H5zm5 3.5V13H9v1.5h1zm0-1.5v-2.063H9V13h1z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M.5.5V0a.5.5 0 00-.5.5h.5zm14 0h.5a.5.5 0 00-.5-.5v.5zm0 8l.354.354A.5.5 0 0015 8.5h-.5zm-3 3v.5a.5.5 0 00.354-.146L11.5 11.5zm-5 0V11a.5.5 0 00-.325.12l.325.38zm-3.5 3h-.5a.5.5 0 00.825.38L3 14.5zm0-3h.5A.5.5 0 003 11v.5zm-2.5 0H0a.5.5 0 00.5.5v-.5zM.5 1h14V0H.5v1zM14 .5v8h1v-8h-1zm.146 7.646l-3 3 .708.708 3-3-.708-.708zM11.5 11h-5v1h5v-1zm-5.325.12l-3.5 3 .65.76 3.5-3-.65-.76zM3.5 14.5v-3h-1v3h1zM3 11H.5v1H3v-1zm-2 .5V.5H0v11h1zM10 3v5h1V3h-1zM7 3v5h1V3H7z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M9.5 0v11A3.5 3.5 0 116 7.5m8-2A4.5 4.5 0 019.5 1" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M1.61 12.738l-.104.489.105-.489zm11.78 0l.104.489-.105-.489zm0-10.476l.104-.489-.105.489zm-11.78 0l.106.489-.105-.489zM6.5 5.5l.277-.416A.5.5 0 006 5.5h.5zm0 4H6a.5.5 0 00.777.416L6.5 9.5zm3-2l.277.416a.5.5 0 000-.832L9.5 7.5zM0 3.636v7.728h1V3.636H0zm15 7.728V3.636h-1v7.728h1zM1.506 13.227c3.951.847 8.037.847 11.988 0l-.21-.978a27.605 27.605 0 01-11.568 0l-.21.978zM13.494 1.773a28.606 28.606 0 00-11.988 0l.21.978a27.607 27.607 0 0111.568 0l.21-.978zM15 3.636c0-.898-.628-1.675-1.506-1.863l-.21.978c.418.09.716.458.716.885h1zm-1 7.728a.905.905 0 01-.716.885l.21.978A1.905 1.905 0 0015 11.364h-1zm-14 0c0 .898.628 1.675 1.506 1.863l.21-.978A.905.905 0 011 11.364H0zm1-7.728c0-.427.298-.796.716-.885l-.21-.978A1.905 1.905 0 000 3.636h1zM6 5.5v4h1v-4H6zm.777 4.416l3-2-.554-.832-3 2 .554.832zm3-2.832l-3-2-.554.832 3 2 .554-.832z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7.5 1c-1.155 0-2.174.412-2.894 1.281-.642.775-1.006 2.35-1.066 3.666l-.073.01-.022.004a8.68 8.68 0 00-.368.059c-.465.089-1.346.326-1.543 1.277-.093.445.011.833.247 1.134.211.269.497.429.708.53.09.041.181.08.274.117-.21.584-.579 1.184-.987 1.728-.382.508-.28 1.153-.083 1.573.197.421.57.402 1.192.43.352.015.722.051 1.09.12.166.03.362.098.606.2.142.06.283.123.423.187.113.052.235.106.374.167.573.25 1.276.517 2.056.517s1.483-.267 2.055-.517c.14-.06.26-.115.375-.167l.025-.012c.135-.06.26-.117.398-.174.243-.103.44-.17.606-.201a7.951 7.951 0 011.09-.12c.622-.028 1.104-.009 1.303-.43.197-.42.298-1.065-.084-1.573-.406-.54-.772-1.136-.983-1.716a5.24 5.24 0 00.305-.127c.216-.098.518-.261.73-.543.245-.326.315-.739.175-1.184-.28-.886-1.092-1.122-1.568-1.216a6.857 6.857 0 00-.355-.058l-.012-.002-.056-.009c-.065-1.234-.41-2.795-1.036-3.581C9.695 1.485 8.682 1 7.5 1z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M4.5 13.5l3-7m-3.236 3a2.989 2.989 0 01-.764-2V7A3.5 3.5 0 017 3.5h1A3.5 3.5 0 0111.5 7v.5a3 3 0 01-3 3 2.081 2.081 0 01-1.974-1.423L6.5 9m1 5.5a7 7 0 110-14 7 7 0 010 14z" stroke="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7.5 1.5l.121-.485A.5.5 0 007 1.5h.5zm5.5 8c0 .774-.55 1.641-1.583 2.343C10.4 12.533 8.998 13 7.5 13v1c1.696 0 3.294-.525 4.479-1.33C13.148 11.876 14 10.743 14 9.5h-1zM7.5 13c-1.498 0-2.9-.466-3.917-1.157C2.551 11.14 2 10.273 2 9.5H1c0 1.243.852 2.376 2.021 3.17C4.206 13.475 5.804 14 7.5 14v-1zM2 9.5c0-.774.55-1.641 1.583-2.343C4.6 6.467 6.002 6 7.5 6V5c-1.696 0-3.294.525-4.479 1.33C1.852 7.124 1 8.257 1 9.5h1zM7.5 6c1.498 0 2.9.467 3.917 1.157C12.449 7.86 13 8.727 13 9.5h1c0-1.243-.852-2.376-2.021-3.17C10.794 5.525 9.196 5 7.5 5v1zm2.306 4.54c-.69.29-1.32.46-2.306.46v1c1.136 0 1.898-.204 2.694-.54l-.388-.92zM7.5 11c-.987 0-1.617-.17-2.306-.46l-.388.92c.796.336 1.558.54 2.694.54v-1zM8 5.5v-4H7v4h1zm-.621-3.515l4 1 .242-.97-4-1-.242.97zM3.974 6.841c-.286-.855-1.12-1.297-1.952-1.297v1c.51 0 .886.261 1.004.615l.948-.318zM2.022 5.544A2.022 2.022 0 000 7.566h1a1.02 1.02 0 011.022-1.022v-1zM0 7.566C0 8.589.76 9.424 1.74 9.56l.139-.99A1.016 1.016 0 011 7.565H0zm11.974-.407c.118-.354.493-.615 1.004-.615v-1c-.832 0-1.666.442-1.952 1.297l.948.318zm1.004-.615A1.02 1.02 0 0114 7.566h1a2.022 2.022 0 00-2.022-2.022v1zM14 7.566c0 .511-.38.934-.879 1.004l.139.99A2.016 2.016 0 0015 7.567h-1zM12.5 3a.5.5 0 01-.5-.5h-1A1.5 1.5 0 0012.5 4V3zm.5-.5a.5.5 0 01-.5.5v1A1.5 1.5 0 0014 2.5h-1zm-.5-.5a.5.5 0 01.5.5h1A1.5 1.5 0 0012.5 1v1zm0-1A1.5 1.5 0 0011 2.5h1a.5.5 0 01.5-.5V1zM5 9h1V8H5v1zm4 0h1V8H9v1z" fill="currentColor"></path></svg> </a> <a href="javascript:" target="_blank" rel="noopener" aria-label="Tiktok Link" class="footer-social-account-link transition-transform hover:scale-105 active:scale-100"> <svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11.5 13.5l-.326.379a.5.5 0 00.342.12L11.5 13.5zm-1.066-1.712a.5.5 0 00-.785.62l.785-.62zm.398-.41l-.174-.468a.672.672 0 00-.02.007l.194.461zm-1.738.516L9.01 11.4l-.008.001.092.492zm-3.104-.012l-.095.49.003.001.092-.491zm-1.762-.515l-.182.465.182-.466zm-.875-.408l-.278.415a.46.46 0 00.033.021l.245-.436zm-.108-.06l.277-.416a.491.491 0 00-.054-.031l-.223.447zm-.048-.036l.353-.354a.502.502 0 00-.11-.083l-.243.437zm2.154 1.52a.5.5 0 00-.785-.62l.785.62zM3.5 13.5l-.016.5a.5.5 0 00.347-.125L3.5 13.5zm-3-2.253H0a.5.5 0 00.006.08l.494-.08zm1.726-8.488l-.3-.4a.5.5 0 00-.168.225l.468.175zM5.594 1.5l.498-.047A.5.5 0 005.605 1l-.01.5zm-.378 1.306a.5.5 0 00.996-.095l-.996.095zm3.526-.063a.5.5 0 00.992.127l-.992-.127zM9.406 1.5L9.395 1a.5.5 0 00-.485.436l.496.064zm3.368 1.259l.468-.175a.5.5 0 00-.168-.225l-.3.4zm1.726 8.488l.494.08a.497.497 0 00.006-.08h-.5zM6.481 8.8l-.5-.008V8.8h.5zm5.019 4.7l.326-.379-.002-.002a.794.794 0 01-.044-.038 21.355 21.355 0 01-.536-.48c-.325-.298-.66-.622-.81-.813l-.785.62c.208.264.603.64.918.93a29.109 29.109 0 00.593.53l.01.008.003.002.327-.378zm.436-3.246c-.46.303-.894.513-1.278.656l.348.937a7.352 7.352 0 001.48-.758l-.55-.835zm-1.297.663a7.387 7.387 0 01-1.629.484l.168.986a8.39 8.39 0 001.848-.548l-.387-.922zm-1.637.485a7.895 7.895 0 01-2.92-.012l-.184.983a8.896 8.896 0 003.288.012l-.184-.983zm-2.917-.011a9.57 9.57 0 01-1.675-.49l-.364.931c.512.2 1.13.402 1.849.54l.19-.981zm-1.675-.49a6.536 6.536 0 01-.813-.378l-.489.872c.326.183.648.324.938.437l.364-.931zm-.78-.358a.802.802 0 00-.108-.061c-.02-.01-.011-.007 0 .001l-.555.832a.87.87 0 00.108.061c.021.01.012.007 0-.002l.556-.83zm-.162-.091a.332.332 0 01.082.058l-.707.707c.023.023.081.08.178.13l.447-.895zm-.028-.026a4.697 4.697 0 01-.28-.168l-.011-.008a.025.025 0 00-.001 0l-.287.41-.286.409.001.001.002.002.007.004.021.014.075.049c.064.04.156.096.273.161l.486-.874zm1.126 1.338c-.152.193-.489.525-.813.829a30.38 30.38 0 01-.538.491l-.034.031-.01.008-.001.002h-.001l.331.375.331.375.001-.001.003-.002.01-.009.036-.032a38.039 38.039 0 00.555-.508c.315-.296.708-.677.915-.94l-.785-.62zM3.516 13c-1.166-.037-1.778-.521-2.11-.96a2.394 2.394 0 01-.4-.82 1.1 1.1 0 01-.013-.056v.002l-.493.08c-.494.08-.494.08-.493.081v.006a1.367 1.367 0 00.028.127 3.394 3.394 0 00.573 1.183c.505.667 1.393 1.31 2.876 1.357l.032-1zM1 11.247c0-1.867.42-3.94.847-5.564a35.45 35.45 0 01.776-2.552 16.43 16.43 0 01.067-.186l.004-.01v-.001l-.468-.175-.469-.175v.001l-.001.003-.004.011a9.393 9.393 0 00-.072.2 36.445 36.445 0 00-.8 2.629C.443 7.083 0 9.253 0 11.247h1zm1.526-8.088c.8-.6 1.577-.89 2.15-1.03a4.764 4.764 0 01.86-.128A1.48 1.48 0 015.585 2h-.001l.01-.5.01-.5H5.6a.848.848 0 00-.028 0h-.068a3.973 3.973 0 00-.24.016 5.763 5.763 0 00-.825.141 6.938 6.938 0 00-2.513 1.2l.6.8zm2.57-1.612l.12 1.259.996-.095-.12-1.258-.996.094zM9.734 2.87l.168-1.306-.992-.128-.168 1.307.992.127zM9.406 1.5l.01.5h-.001a.497.497 0 01.049 0c.038.002.1.005.179.013.16.014.394.047.681.117a5.94 5.94 0 012.15 1.029l.6-.8a6.937 6.937 0 00-2.513-1.2 5.76 5.76 0 00-.825-.142A3.98 3.98 0 009.399 1h-.003l.01.5zm3.368 1.259l-.469.174.001.003.004.009.013.037.053.149a35.482 35.482 0 01.777 2.552c.428 1.624.847 3.697.847 5.564h1c0-1.994-.444-4.164-.88-5.819a36.512 36.512 0 00-.8-2.629 15.246 15.246 0 00-.057-.158l-.015-.042-.004-.01-.001-.004-.47.174zm1.726 8.488l-.493-.08v-.003l-.002.008-.01.047c-.012.045-.03.113-.061.197-.062.17-.167.396-.34.624-.332.439-.944.923-2.11.96l.032 1c1.483-.047 2.37-.69 2.876-1.356a3.395 3.395 0 00.573-1.184 2.05 2.05 0 00.026-.118l.002-.01v-.004c0-.001 0-.002-.493-.081zM5.259 6.97c-1.002 0-1.723.867-1.723 1.83h1c0-.498.357-.83.723-.83v-1zM3.536 8.8c0 .967.736 1.83 1.723 1.83v-1c-.357 0-.723-.334-.723-.83h-1zm1.723 1.83c1 0 1.722-.866 1.722-1.83h-1c0 .5-.357.83-.722.83v1zM6.98 8.81c.016-.978-.728-1.84-1.722-1.84v1.001c.372 0 .73.338.722.822l1 .017zm2.653-1.84c-1.002.001-1.723.868-1.723 1.831h1c0-.498.357-.83.723-.83v-1zM7.91 8.802c0 .967.736 1.83 1.723 1.83v-1c-.357 0-.723-.334-.723-.83h-1zm1.723 1.83c1 0 1.722-.866 1.722-1.83h-1c0 .5-.357.83-.722.83v1zm1.722-1.83c0-.963-.721-1.83-1.722-1.83v1c.365 0 .722.332.722.83h1zM3.74 4.44c1.443-.787 2.619-1.154 3.763-1.155 1.145 0 2.318.365 3.758 1.154l.48-.876c-1.522-.835-2.865-1.279-4.238-1.278-1.373 0-2.717.445-4.241 1.277l.478.878z" fill="currentColor"></path></svg> </a> </div> <span class="footer-primary-site-title text-two text-base font-normal">2024 <span class="text-sm opacity-70">•</span> <a href="https://yehudakatz.com" class="font-bold">Katz Got Your Tongue</a></span> </div> </footer> <script src="https://yehudakatz.com/assets/js/exclude/cards.js?v=4365bbbe46" defer></script> <script src="https://yehudakatz.com/assets/js/exclude/fslightbox.min.js?v=4365bbbe46" defer></script> <script src="https://yehudakatz.com/assets/js/lib/lazyhedwik.js?v=4365bbbe46"></script> <script src="https://yehudakatz.com/assets/built/main.min.js?v=4365bbbe46"></script> <script> function initPopover(){let e=document.getElementById("popoverButton"),s=document.getElementById("hedwikPopover");window.addEventListener("scroll",()=>{s.classList.contains("popover-show")&&(s.classList.remove("popover-show"),s.classList.add("popover-hide"),setTimeout(()=>{s.style.display="none"},150))}),e.addEventListener("click",()=>{s.classList.contains("popover-show")?(s.classList.remove("popover-show"),s.classList.add("popover-hide"),setTimeout(()=>{s.style.display="none"},150)):(s.style.display="block",s.classList.remove("popover-hide"),s.classList.add("popover-show"),s.style.left=`${e.offsetLeft+(e.offsetWidth-s.offsetWidth)/2}px`,s.style.top=`${e.offsetTop+e.offsetHeight+10}px`)}),document.addEventListener("click",o=>{e.contains(o.target)||s.contains(o.target)||(s.classList.remove("popover-show"),s.classList.add("popover-hide"),setTimeout(()=>{s.style.display="none"},150))})}initPopover(); </script> <script> const scrollLine = document.querySelector('.scroll-line'); function fillScrollLine() { const windowHeight = window.innerHeight; const fullHeight = document.body.clientHeight; const scrolled = window.scrollY; const percentScrolled = (scrolled / (fullHeight - windowHeight)) * 100; scrollLine.style.width = `${percentScrolled}%`; } window.addEventListener('scroll', debounce(fillScrollLine)); function debounce(func, wait = 15, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } function copyPageLink(){let e=window.location.href,t=document.createElement("textarea");t.value=e,document.body.appendChild(t),t.select(),document.execCommand("copy"),document.body.removeChild(t);let o=document.querySelector(".share-button"),n=document.querySelector(".copying"),c=document.querySelector(".copied"),d=o.querySelector("span");n.classList.add("hidden"),c.classList.remove("hidden"),d.textContent="Copied",setTimeout(()=>{n.classList.remove("hidden"),c.classList.add("hidden"),d.textContent="Copy Link"},2e3)}const shareButton=document.querySelector(".share-button");shareButton.addEventListener("click",copyPageLink); </script> <script defer src="https://ghostboard.io/t/5cc01a81a803662c9f6bab1d.js" type="text/javascript" async></script><noscript><img src="https://ghostboard.io/api/noscript/5cc01a81a803662c9f6bab1d/pixel.gif" alt="" border="0" /></noscript> <script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" referrerpolicy="no-referrer"></script> <script defer> var tabs = new Tabby('[data-tabs]'); </script> </body> </html>