Hiccdown Development Notes

Dennis Hackethal started this discussion over 2 years ago.

Active ideas (2)·​Search·​Activity

Notes about developing the Ruby gem Hiccdown.

  Log in or sign up to participate in this discussion.
With an account, you can revise, criticize, and comment on ideas, and submit new ideas.

New activity, such as comments or revisions, ‘revives’ archived ideas.
Discussions can branch out indefinitely. You may need to scroll sideways.
Dennis Hackethal’s avatar
Dennis HackethalOP·​#1978·​·​Criticism
3rd of 3 versions

Hiccdown methods should live in Rails helpers as instance methods.

1 pending criticismArchivedLinked to by #333 and #1980
Dennis Hackethal’s avatar
Dennis HackethalOP·​#301·​·​Criticism of an earlier version

That isn’t a good idea because Hiccdown methods often share the same conventional names (index, show, etc), which can and does lead to conflict.

ArchivedLinked to by #5851
Dennis Hackethal’s avatar
Dennis HackethalOP·​#1980·​·​Criticism
2nd of 2 versions

Hiccdown methods should live in Rails helpers as class methods. That way, the problem described in #302 is solved – methods can be referenced unambiguously:

ruby
ProductsHelper.index
StoresHelper.index
2 pending criticismsArchivedLinked to by #5851
Dennis Hackethal’s avatar
Dennis HackethalOP·​#312·​·​Criticism of an earlier version

That would be mixing class methods an instance methods in Rails helper modules, which typically only contain instance methods. Not idiomatic Rails usage.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#332·​·​Criticism of an earlier version

#327 applies here, too: no access to instance variables inside helper class methods.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#305·​·​Criticism of an earlier version
2nd of 2 versions

Does that mean they wouldn’t have access to the view_context? If so, calling helper methods from inside these class methods wouldn’t be possible.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#1982·​·​Criticism
5th of 5 versions

Hiccdown methods should live in their own, separate classes. How about they are called ‘displays’?

ruby
class ProductsDisplay
def index vc, # …
vc.some_helper_method
end
end

Behind the scenes, the Hiccdown gem would need to make the instance variables available to the display class:

ruby
display = @display_module.new
view_context.instance_variables.each do |iv|
display.instance_variable_set(
iv,
view_context.instance_variable_get(iv)
)
end

Then:

ruby
class ProductsDisplay
def index vc, # …
vc.some_helper_method(@products)
end
end
1 pending criticismArchivedLinked to by #331 and #5851
Dennis Hackethal’s avatar
Dennis HackethalOP·​#326·​·​Criticism of an earlier version

I’m trying this now. Having to prepend every invocation of a helper method with vc. is getting really old really fast.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#317·​·​Criticism of an earlier version
2nd of 2 versions

Then how would you call index from a helper method?

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#333·​

Having explored three different ideas, I believe #302 – having regular helper methods to render Hiccdown structures – is the best.

The idea is not without its flaws, but having to qualify a method name by, say, calling it idea_form instead of form is still better than manually having to pass the view context around all the time and not being able to trivially access instance variables.

So I’ll stick with #302 for now, which is the status quo already.

ArchivedLinked to by #5851
Dennis Hackethal’s avatar
Dennis HackethalOP·​#335·​
2nd of 2 versions

I think the thing I’m really fighting here is Rails being object-oriented. Which I can’t do anything about.

Not sure the Rails team realizes how much OOP reduces the extensibility of Rails.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#859·​·​Criticism

Could the errors around layouts be related to this?

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5848·​·​Criticism·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#1984·​·​Criticism

Hiccdown should have support for ids and class names in the tag symbol. Like Hiccup.

ruby
[:'div#my-id.my-class.another-class']
# => <div id="my-id" class="my-class another-class"></div>

It should also allow mixing:

ruby
[:'div#my-id.my-class.another-class', {id: 'override', class: 'additive'}]
# => <div id="override" class="my-class another-class additive"></div>

In other words, the id from the hash would override the id from the symbol, and the class from the hash would be added to the classes from the symbol.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5852·​·​Criticism·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#4328·​

Could the application layout live in ApplicationHelper#layout?

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5843·​·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#4329·​

Is there a way to teach user-built helpers how to process Hiccdown? Or maybe intercepting capture already took care of this?

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5844·​·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#4330·​·​Criticism

Redirects result in two additional requests, the first of which is a turbo-stream request that renders nothing, thus (presumably) prompting the browser to make another request for the same resource.

This? https://stackoverflow.com/a/74071278

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5849·​·​Criticism·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#4331·​
Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5850·​·​AI-assisted

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.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#4333·​·​Criticism

When an empty block is passed to render, it results in an empty tag '<>'

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5838·​·​Criticism

Fixed as of c36fa5c, version 1.5.2.

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5839·​·​Criticism·​AI-assisted

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.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5841·​·​Criticism·​AI-assisted

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

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5840·​·​Criticism·​AI-assisted

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.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5842·​·​Criticism·​AI-assisted

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

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5845·​·​Criticism·​AI-assisted

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.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5846·​·​Criticism·​AI-assisted

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

Archived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5847·​·​Criticism·​AI-assisted

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.

1 pending criticismArchived
Dennis Hackethal’s avatar
Dennis HackethalOP·​#5851·​·​Criticism·​AI-assisted

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.

Archived
  • Rationally adoptable.
  • Not rationally adoptable.
  • Older versions with pending criticisms.
  • Ideas are blue, criticisms red, comments small and gray.
  • Hover over ideas to expand them.
  • Click on ideas to jump to their place in the discussion.