Search

Ideas that are…

Search Ideas

Superseded by #3841.

#5863​·​Dennis HackethalOP, about 1 hour ago​·​Criticism

How Do Bounties Work?

Bounties let you invite criticism and reward high-quality contributions with real money.

Bounties are in beta. Expect things to break.

How do I participate?

Only users with a Beginner, Engager, and Defender badge can participate in bounties. Find a list of all badges and how to get them here.

To participate in a bounty, start by logging in or signing up.

Next, browse the list of bounties. Click a bounty to view its page, review the bountied idea and the terms, and submit a criticism of that idea.

That’s it – you’re in.

How do I get paid?

The bounty owner reviews submissions for eligibility against his bounty terms.

To be eligible for a payout, all of the following must be true:

  1. Your submission is a direct criticism of the bountied idea.
  2. Your submission has no pending counter-criticisms by its deadline. (For temporary bounties, that’s when submissions close and the review period begins; for standing bounties, it’s seven days after submission.)
  3. Your submission meets the bounty terms and the site-wide terms.
  4. You’ve connected a Stripe account in good standing before payouts are made. (For temporary bounties, that’s when the review period ends; for standing bounties, it’s seven days after submission.)
  5. You’ve not contributed funds to the bounty.

The bounty owner is never eligible to receive payouts from their own bounty.

Note that counter-criticisms are not constrained by the bounty-specific terms. Only direct criticisms of the bountied idea are.

How much will I get paid?

For temporary bounties, the amount is prorated among eligible participants based on contribution. For example, if there are ten eligible criticisms and you contributed two of them, you receive 20% of the amount when the bounty ends.

For standing bounties, amounts are assigned on a per-submission basis. For example, funders may indicate that they will pay a total of USD 100 for the first eligible submission, a total of USD 50 for the second eligible submission, and so on. Each eligible submission has its own payout date.

Fractions of cents are not paid out.

How do I run a bounty?

Open the menu (⋯) next to an idea and choose Start a bounty. You need the Beginner and Defender badges to start one.

Choose a temporary or standing bounty and write clear terms describing the kinds of criticisms you’re willing to pay for. Then set an amount and enter your credit-card details to authorize it plus a 5% bounty fee.

Your card is at most authorized, but not charged, when the bounty starts.

A temporary bounty typically runs for five to seven days, depending on your card’s authorization window. You may review submissions during the entire bounty period. Toward the end, a 24-hour grace period begins during which no new submissions can be made but you may continue your review. Reject any submissions that don’t meet your terms. Submissions you don’t reject are automatically accepted at the end of the review period and become eligible for payout. Your card is then charged the full authorization.

A standing bounty runs for as long as funds last. Each submission has its own seven-day review period. Again, reject any submissions that don’t meet your terms. Submissions you don’t reject are automatically accepted seven days after submission. Your card is then charged as indicated in your funding allocation.

If you reject all submissions, or none are eligible, your card is never charged.

What’s the difference between a temporary and a standing bounty?

A temporary bounty has a fixed duration, typically between five and seven days. The bounty amount is prorated among eligible participants at the end. Standing bounties, on the other hand, don’t have a fixed duration; they run as long as funds last. Funds are paid out continuously and on a per-submission basis, as described above.

Temporary bounties are ideal when you have limited time and a smaller budget. Standing bounties are ideal for the long term with a larger budget. However, you can mix and match based on your own unique preferences and circumstances: for example, it’s possible to use a larger budget on a temporary bounty.

Can I fund someone else’s bounty?

Yes. Review the bounty terms. If you agree with them, click the ‘Add funding’ button on the bounty page and follow the next steps. At this point, your card is at most authorized but not charged.

The bounty owner will expect you to join them in enforcing their terms. Don’t fund a bounty if you’re not prepared to enforce its terms. Run a separate bounty with your own terms instead. By funding someone else’s bounty, you agree that your card may be charged in full if you’re found unwilling to enforce the bounty terms, with all proceeds going to Veritula.

Your card is charged for any submissions the bounty owner does not reject. If he rejects all submissions, your card is not charged.

Funders are never eligible to receive payouts from a bounty they funded.

Start a bounty today. Terms apply.

#5861​·​Dennis HackethalOP revised about 1 hour ago​·​AI-assisted​·​Original #3517

Measured on Veritula locally, on a discussion of 213 ideas:

  • The whole discussion page, which a Turbo refresh re-renders: 211 ms and 506 KB, of which loading the whole tree (Idea.tree) takes 18 ms and Hiccdown.to_html 35 ms.
  • One idea's own markup, without its replies: 0.4 ms (median; 0.9 ms at the 90th percentile).
  • One idea's HTML with its replies: 3.3 KB (median).

A component request would cost the request's fixed overhead, loading that idea's part of the tree (18 ms at most) and about a millisecond of rendering: an estimated 10–30 ms and a few KB, about ten times faster and a hundred times smaller than the whole page. The whole page grows with the discussion while a component stays about the same, and the browser morphing 500 KB of DOM, which I didn't measure, comes on top.

#5860​·​Dennis HackethalOP, 2 days ago​·​AI-assisted

Make Hiccdown reactive, like Reagent: a helper method as the first element marks a component, as in [method(:product_card), @product]. A Method knows its module and name, so a later request can call ProductsHelper#product_card again for the same records.

The server keeps track of which components each page rendered, with which records, and gives each component's element the id dom_id(record), as turbo streams do. When a record changes (after_commit, checking that it actually changed), the server tells the pages showing it, over Action Cable. Each page then requests just that component, which renders in the viewer's own request, with their user, session, locale, time zone and CSRF token, and swaps it in with a turbo stream.

Rendering the component in the request that changed the record would save that round trip, but that request has none of the viewers' context: it would render for the wrong user, or have to rebuild each viewer's context and render once per viewer.

A first pass can skip checking whether a change affects what a component shows, as when a product's name changes on a page that only shows its price.

#5858​·​Dennis HackethalOP revised 2 days ago​·​AI-assisted​·​Original #4332

On large pages, like Veritula's discussions, that's too expensive: a refresh re-renders the whole page for every viewer, sends all of it, and morphs all of it, for a change to one idea. Components re-render, send and morph only what changed, so they'd give the same result as morphing on pages where morphing the whole page is too slow to use.

#5857​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​Criticism

Procs can't be found again in a later request. The lambda a helper built while rendering the page is gone by the time the page asks for the component, so the server has no way to call it again.

#5856​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​Criticism

Turbo 8 already does most of this. With broadcasts_refreshes and morphing, each viewer's page re-renders in their own request when a record changes, keeping scroll position and form state. Components would only save rendering and sending the rest of the page.

#5855​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismCriticized1

Make Hiccdown reactive, like Reagent: a proc as the first element marks a component, as in [->(product) { [:li, product.name] }, @product].

The server keeps track of which components each page rendered, with which records, and gives each component's element the id dom_id(record), as turbo streams do. When a record changes (after_commit, checking that it actually changed), the server tells the pages showing it, over Action Cable. Each page then requests just that component, which renders in the viewer's own request, with their user, session, locale, time zone and CSRF token, and swaps it in with a turbo stream.

Rendering the component in the request that changed the record would save that round trip, but that request has none of the viewers' context: it would render for the wrong user, or have to rebuild each viewer's context and render once per viewer.

A first pass can skip checking whether a change affects what a component shows, as when a product's name changes on a page that only shows its price.

#5853​·​Dennis HackethalOP revised 2 days ago​·​AI-assisted​·​Original #4332​·​Criticized2

Done as of 3a304be, to be released in version 1.6.0, as described here. The id and classes can come in any order after the tag name, and two ids in one tag raise ArgumentError.

#5852​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

Known since #301, and accepted in #333: the alternatives, helper class methods (#1980) and separate display classes (#1982), were worse. Qualifying a helper method's name, like items_page rather than calling index, is the way around it.

#5851​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

They already do: turbo-rails gives frame requests its own minimal layout, and Hiccdown leaves it be. As of c5ce76f, to be released in version 1.6.0, a test checks that frame requests keep Turbo's frame layout even where ApplicationHelper#layout exists, and a system test (e71df95) loads a frame in a browser.

#5850​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​Archived

Doesn't reproduce as of e71df95. In the app in test/dummy, a form whose action redirects takes one POST and one GET in a headless browser, which a system test now checks.

#5849​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

Probably, and they're fixed. Hiccdown used to force a layout (layout: true, then layout: !request.format.turbo_stream?), overriding the layout method turbo-rails relies on, and turbo-stream requests failed with "no default layout" (fef00cb). cf92f06, in version 1.5.2, stopped forcing a layout. As of c5ce76f and 4a087e4, to be released in version 1.6.0, tests check that frame requests keep Turbo's frame layout and that turbo streams get none, and the app in test/dummy shows it in a browser.

#5848​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

A helper method that calls another action's helper method by name can get another helper's. Rails mixes every helper into every view, so calling index from ItemsHelper#create runs whichever helper's index Rails included last, such as PagesHelper#index:

ruby
module ItemsHelper
def index
[:ul, @items.map { |i| [:li, i.name] }]
end
def create
index # PagesHelper#index, not ItemsHelper#index
end
end

Action names like index, show and edit are in many helpers, and the Readme encourages helper methods calling one another.

#5847​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismCriticized1Archived

Fixed as of 99b6280, to be released in version 1.6.0.

#5846​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

On Rails 8.1, Hiccdown's capture hands keyword arguments to the block as key-value pairs rather than as keywords. capture(text: 'foo') { |text:| text } raises ArgumentError: missing keyword: :text instead of returning 'foo', and a block that takes them as a hash, { |options| … }, gets [:text, 'foo'] instead of {text: 'foo'}.

Earlier versions of Rails pass keywords on to the block as a hash, which Hiccdown leaves alone.

#5845​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismCriticized1Archived

Mostly, intercepting capture already took care of it: helpers of your own that pass a block on to Rails' helpers, or return Hiccdown, worked. Hiccdown given to Rails' helpers as content didn't, as in content_tag(:div, yield), tag.div([:span, 'foo']) or link_to([:span, 'foo'], path). As of 134deb9, to be released in version 1.6.0, it does.

#5844​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​Archived

Yes. As of 4fd7a0e, to be released in version 1.6.0, the layout named X lives in XHelper#layout, just as it would in layouts/X.html.erb, so the application layout lives in ApplicationHelper#layout. yield and yield(:title) work as in an ERB layout, and layout files keep working for a gradual migration.

#5843​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​Archived

Fixed as of 31cf174, to be released in version 1.6.0.

#5842​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

Fixed as of 9c46c61, to be released in version 1.6.0.

#5841​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismArchived

Hiccdown finds an action's helper method through params[:action] rather than the controller's action_name. When a controller runs without the router, as in ProductsController.action(:show).call(env), params[:action] is nil and rendering raises NoMethodError: undefined method 'to_sym' for nil instead of calling ProductsHelper#show.

#5840​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismCriticized1Archived

Calling render with options only, such as render status: :not_found or render layout: 'admin', skips the action's helper method and looks for an ERB template instead. Without one, it raises ActionView::MissingTemplate:

ruby
class ProductsController < ApplicationController
def show
render status: :not_found # MissingTemplate, though ProductsHelper#show exists
end
end

Naming the action works around it: render :show, status: :not_found.

#5839​·​Dennis HackethalOP, 2 days ago​·​AI-assisted​·​CriticismCriticized1Archived

Fixed as of c36fa5c, version 1.5.2.

#5838​·​Dennis HackethalOP, 2 days ago​·​CriticismArchived

Fixed as of ed8868b, version 1.3.1.

#5837​·​Dennis HackethalOP, 3 days ago​·​CriticismArchived