Code Monkey home page Code Monkey logo

helpers's Introduction

Hanami::Helpers

View helpers for Ruby applications

Status

Gem Version CI Test Coverage Depfu Inline Docs

Contact

Rubies

Hanami::Helpers supports Ruby (MRI) 3.0+

Installation

Add this line to your application's Gemfile:

gem 'hanami-helpers'

And then execute:

$ bundle

Or install it yourself as:

$ gem install hanami-helpers

Usage

Hanami::Helpers offers a set of utilities to enrich web views.

HTML helper

HTML5 markup generator (#html).

View:

module Users
  class Show
    include Hanami::Helpers

    def sidebar
      html.aside(id: 'sidebar') do
        p "Languages", class: 'title'

        ul do
          li "Italian"
          li "English"
        end
      end
    end
  end
end

Template:

<%= sidebar %>

Output:

<aside id="sidebar">
  <p class="title">Languages</p>

  <ul>
    <li>Italian</li>
    <li>English</li>
  </ul>
</aside>

Form Helper

Form generator for HTML5 (#form_for)

Template Usage

Template:

<%=
  form_for :book, routes.books_path do
    text_field :title

    submit 'Create'
  end
%>

Output:

<form action="/books" method="POST" accept-charset="utf-8" id="book-form">
  <input type="text" name="book[title]" id="book-id" value="">
  <button type="submit">Create</button>
</form>

View Usage

View:

module Books
  class New
    include Hanami::Helpers

    def form
      form_for :book, routes.books_path do
        text_field :title

        submit 'Create'
      end
    end
  end
end

Template:

<%= form %>

Output:

<form action="/books" method="POST" accept-charset="utf-8" id="book-form">
  <input type="text" name="book[title]" id="book-id" value="">
  <button type="submit">Create</button>
</form>

Reuse Code

Views:

module Books
  class New
    include Hanami::Helpers

    def form
      Form.new(:book, routes.books_path)
    end

    def submit_label
      'Create'
    end
  end

  class Edit
    include Hanami::Helpers

    def form
      Form.new(:book, routes.book_path(id: book.id), {book: book}, {method: :patch})
    end

    def submit_label
      'Update'
    end
  end
end

Templates:

# books/new.html.erb
<%= render partial: 'books/form' %>
# books/edit.html.erb
<%= render partial: 'books/form' %>
# books/_form.html.erb
<%=
  form_for form, class: 'form-horizontal' do
    text_field :title

    submit submit_label
  end
%>

Output for new:

<form action="/books" method="POST" accept-charset="utf-8" id="book-form">
  <input type="text" name="book[title]" id="book-id" value="">
  <button type="submit">Create</button>
</form>

Output for edit:

<form action="/books/23" method="POST" accept-charset="utf-8" id="book-form">
  <input type="hidden" name="_method" value="PATCH">
  <input type="text" name="book[title]" id="book-id" value="TDD">
  <button type="submit">Update</button>
</form>

Escape helper

HTML (#h), HTML attribute (#ha) and URL (#hu) escape helpers.

View:

module Users
  class Show
    include Hanami::Helpers

    def home_page_link
      %(<a href="#{ hu(user.home_page_url) }" title="#{ ha(user.name} }'s website">#{ h(user.website_name) }</a>)
    end

    def code_snippet
      raw user.code_snippet
    end
  end
end

Template:

<%= home_page_link %>
<%= code_snippet %>

Output:

<a href="https://example.org" title="Maria's website">My Blog</a>
<code>puts "Hello, World!"</code>

Routing Helper

Hanami and Hanami::Router integration (#routes).

View:

module Home
  class Index
    include Hanami::Helpers

    def link_to_home
      %(<a href="#{ routes.home_path }">Home</a>)
    end
  end
end

Template:

<%= link_to_home %>

Output:

<a href="/">Home</a>

Number Formatting Helper

Format numbers (#format_number).

View:

module Home
  class Index
    include Hanami::Helpers

    def visitors_count
      format_number '1000'
    end
  end
end

Template:

<p><%= visitors_count %></p>

Output:

<p>1,000</p>

Philosophy

All the Hanami helpers are modules to include.

Most of the time they inject private methods. This restriction prevents helper methods to be used on the outside (eg. in a template).

We want to encourage developers to use meaningful and simple APIs in their templates.

Bad style example

module Users
  class Show
    include Hanami::Helpers
  end
end
<%= format_number user.followers_count %>

This style increases the complexity of the template and it makes testing hard.

Good style example

module Users
  class Show
    include Hanami::Helpers

    def followers_count
      format_number user.followers_count
    end
  end
end
<%= followers_count %>

This simplifies the markup. In order to test the value that will be printed becomes easier: Users::Show#followers_count.

Versioning

Hanami::Helpers uses Semantic Versioning 2.0.0

Contributing

  1. Fork it ( https://github.com/hanami/helpers/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Copyright

Copyright © 2014-2021 Luca Guidi – Released under MIT License

This project was formerly known as Lotus (lotus-helpers).

helpers's People

Contributors

alfonsouceda avatar arr-dev avatar artofhuman avatar cllns avatar dannysantos avatar davydovanton avatar deepj avatar depfu[bot] avatar gustavocaso avatar hoksilato avatar ilyario avatar jc00ke avatar jodosha avatar josemotanet avatar kasumionlyway avatar khaiql avatar killthekitten avatar lucasallan avatar malin-as avatar morrme avatar myabc avatar nerdinand avatar nickgnd avatar pragtob avatar sebastjan-hribar avatar skyriser avatar solnic avatar titeiko avatar tomkadwill avatar vyper avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

helpers's Issues

form_form - Duplicate option tags generated for select tag

Ruby 3.1.2

Gemfile

gem "hanami", "~> 2.0"
gem "hanami-router", "~> 2.0"
gem "hanami-controller", "~> 2.0"
gem "hanami-validations", "~> 2.0"

gem 'hanami-view', github: "hanami/view", ref: '35a90c2517e0e636e6577c0bcf752bf6a70a9694'

# TODO: Temporary using the `main` branch because using the released version `1.3.3` causes conflict of `hanami-utils` version ( hanami-utils (~> 1.3) ) used by `hanami-controller` version ( hanami-utils (~> 2.0) )
gem 'hanami-helpers', github: "hanami/helpers", branch: 'main'

gem 'hamlit', '~> 3.0', '>= 3.0.3'

Gemfile.lock

GIT
  remote: https://github.com/hanami/helpers.git
  revision: b9e8cf0fdc4599bd89cd374f43d56644c9e4054b
  branch: main
  specs:
    hanami-helpers (2.0.0.alpha1)
      hanami-utils (~> 2.0)

GIT
  remote: https://github.com/hanami/view.git
  revision: 35a90c2517e0e636e6577c0bcf752bf6a70a9694
  ref: 35a90c2517e0e636e6577c0bcf752bf6a70a9694
  specs:
    hanami-view (2.0.0.alpha8)
      concurrent-ruby (~> 1.0)
      dry-configurable (~> 1.0.0.rc)
      dry-core (~> 1.0.0.rc)
      dry-inflector (~> 1.0, < 2)
      tilt (~> 2.0, >= 2.0.6)

my_view.html.haml

In my view I have following code generating a form using form_for helper.

= form_for(form_name, form_url, method: form_method_option_value) do
  .form-control<
    = label('Name')
    = text_field(:name, value: nil)

  .form-control<
    = label('Select Language')
    
    - options = { lang_1: 'Language-1', lang_2: 'Language-2' }
    = select(:language, options)

When the page is rendered in the select tag generated I can seen duplicate options

<form action="TODO" method="POST" accept-charset="utf-8" id="my-entity-form">
<input type="hidden" name="_csrf_token" value="b2142aa129f36d229ac30ca394b648be9152134297284d95632eb848e777b470">
<label for="my-entity-term">Term</label>
<input type="text" name="my-entity[term]" id="my-entity-term" value="">
<label for="my-entity[select-language]">Select Language</label>
<select name="my-entity[language]" id="my-entity-language">
<option value="lang_1">Language-1</option>
<option value="lang_2">Language-2</option>
<option value="lang_1">Language-1</option>
<option value="lang_2">Language-2</option>
</select>
</form>

Experimenting with the code I figured out that the problem is with the helper methods prefixed with =.

Updating the view code in following manner the duplicate options problem is gone:

= form_for(form_name, form_url, method: form_method_option_value) do
  .form-control<
    - label('Name')
    - text_field(:name, value: nil)

  .form-control<
    - label('Select Language')
    
    - options = { lang_1: 'Language-1', lang_2: 'Language-2' }
    - select(:language, options)

But then it raised a question that if using = is the problem then the label and text-field tags should also have been duplicated but that isn't the case. So I tried to debug the source code and added following

  caller_locations(0..20).map do |call|
    puts call.to_s
  end

above values.each do |content, value| line in the block given to super(attributes) in following method:

def select(name, values, attributes = {})
options = attributes.delete(:options) { {} }
multiple = attributes[:multiple]
attributes = {name: _select_input_name(name, multiple), id: _input_id(name)}.merge(attributes)
prompt = options.delete(:prompt)
selected = options.delete(:selected)
input_value = _value(name)
super(attributes) do
option(prompt, disabled: true) if prompt
already_selected = nil
values.each do |content, value|
if (multiple || !already_selected) && (already_selected = _select_option_selected?(value, selected, input_value, multiple))
option(content, {value: value, selected: SELECTED}.merge(options))
else
option(content, {value: value}.merge(options))
end
end
end
end

and then refreshing the page I found following in the server logs:

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:1313:in `block in select'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:63:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'

Above was found when used - before select(:language, options) in the view.

When used = before select(:language, options) in the view following was found:

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:1313:in `block in select'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `block in __tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:1313:in `block in select'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:63:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'

As can be seen in above case following duplicate calls can be seen:

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'

...
...
...

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'

So I found that it is the template's evaluate method duplicate invocation which is causing the behaviour. To be more sure about this conclusion of mine this time I printed the complete call-trace by changing

  caller_locations(0..20).map do |call|
    puts call.to_s
  end

to following

  caller_locations.map do |call|
    puts call.to_s
  end

and retaining = select(:language, options) in the view and I found following:

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:1313:in `block in select'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `block in __tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:39:in `partial'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/render_environment.rb:48:in `partial'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/scope.rb:99:in `render'
/home/jignesh/...../app/templates/...../new.html.haml:5:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/render_environment.rb:44:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view.rb:568:in `call'
/home/jignesh/..../app/view/base.rb:21:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/response.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/extensions/action.rb:92:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/session.rb:43:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/cookies.rb:23:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action.rb:332:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/slice/routing/resolver.rb:78:in `block in resolve_slice_action'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-router-2.0.2/lib/hanami/router.rb:108:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/method_override.rb:24:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/session/abstract/id.rb:266:in `context'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/session/abstract/id.rb:260:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/rack/middleware.rb:36:in `block in call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/clock.rb:15:in `measure'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/rack/middleware.rb:36:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-router-2.0.2/lib/hanami/middleware/app.rb:40:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/slice.rb:758:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/static.rb:161:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/configuration.rb:270:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/request.rb:98:in `block in handle_request'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/thread_pool.rb:340:in `with_force_shutdown'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/request.rb:97:in `handle_request'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/server.rb:431:in `process_client'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/server.rb:233:in `block in run'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/thread_pool.rb:147:in `block in spawn_thread'


/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:1313:in `block in select'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `instance_exec'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:377:in `resolve'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:60:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:63:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/html_node.rb:51:in `content'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_node.rb:46:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `map'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/html_helper/html_builder.rb:336:in `to_s'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/helpers-b9e8cf0fdc45/lib/hanami/helpers/form_helper/form_builder.rb:143:in `to_s'
/home/jignesh/...../app/templates/...../my_view.html.haml:45:in `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:39:in `partial'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/render_environment.rb:48:in `partial'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/scope.rb:99:in `render'
/home/jignesh/...../app/templates/...../new.html.haml:5:in  `__tilt_7880'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `bind_call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:191:in `evaluate'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/tilt-2.1.0/lib/tilt/template.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:49:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/renderer.rb:32:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view/render_environment.rb:44:in `template'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view.rb:568:in `call'
/home/jignesh/..../app/view/base.rb:21:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/response.rb:109:in `render'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/extensions/action.rb:92:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/session.rb:43:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action/cookies.rb:23:in `finish'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-controller-2.0.2/lib/hanami/action.rb:332:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/slice/routing/resolver.rb:78:in `block in resolve_slice_action'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-router-2.0.2/lib/hanami/router.rb:108:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/method_override.rb:24:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/session/abstract/id.rb:266:in `context'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/session/abstract/id.rb:260:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/rack/middleware.rb:36:in `block in call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/clock.rb:15:in `measure'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/dry-monitor-1.0.1/lib/dry/monitor/rack/middleware.rb:36:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-router-2.0.2/lib/hanami/middleware/app.rb:40:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/hanami-2.0.3/lib/hanami/slice.rb:758:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/rack-2.2.7/lib/rack/static.rb:161:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/configuration.rb:270:in `call'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/request.rb:98:in `block in handle_request'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/thread_pool.rb:340:in `with_force_shutdown'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/request.rb:97:in `handle_request'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/server.rb:431:in `process_client'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/server.rb:233:in `block in run'
/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/gems/puma-6.2.2/lib/puma/thread_pool.rb:147:in `block in spawn_thread'

So looking at the complete trace now I see that it is duplicate view call execution which is invoking twice that block inside select method.

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view.rb:568:in `call'

/home/jignesh/.rvm/gems/ruby-3.1.2@my-app/bundler/gems/view-35a90c2517e0/lib/hanami/view.rb:568:in `call'

Can anybody please help me understand such behavior that how just replacing a - with = in the haml file is causing duplicate invocation of view's call method?

And another question which is puzzling me is when using = with label and text_field methods why the duplicate label and input type=text tags were not rendered and the duplication was observed only for select's option tags.

Thanks.

`select` behaves in an unexpected way with `nil` in options

When using nil as a value in the options for a select, unexpected things are happening.

Example:

<%=
  thing = OpenStruct.new(key: 'foo')
  form_for :thing, '/books/1', method: :put, values: {thing: thing} do
    select_options = [
      ['Foo', 'foo'],
      ['Bar', 'bar'],
      ['N/A', nil]
    ]
    select :key, select_options
    submit 'Save'
  end
%>

Results in:

<form action="/books/1" method="POST" accept-charset="utf-8" id="thing-form">
<input type="hidden" name="_method" value="PUT">
<select name="thing[key]" id="thing-key">
<option value="foo" selected="selected">Foo</option>
<option value="bar">Bar</option>
<option value="" selected="selected">N/A</option>
</select>
<button type="submit">Save</button>
</form>

Note that two values are selected: The correct one (Foo) as well as the value for nil (N/A).

Visually, this results in the wrong value being selected in the UI:
web

Unable to insert 2 html5 methods in to form_for block

Im trying to add 2 bootstrap radio buttons which is require nesting in label tag:

<div class="radio">
  <label><input type="radio" name="optradio">Option 1</label>
</div>

Its possible to do only with html helper (or with raw html insert in to form_for, which looks like not possible also). So I create method in view like so:

def radio_btn(radio_name, text_value)
      html.div(class: 'form-group') do
        div(class: 'radio') do
          label do
            input(name: radio_name, type: 'radio', value: radio_name)
            text(text_value)
          end
        end
      end
    end

and trying to render this template:

<%= form_for :message, routes.root_path do
  radio_btn('name1', 'Some text 1')
  radio_btn('name2', 'Some text 2')
end %>

but generated html include only last method call:

<body>
    <div class="container">
      <form action="/" method="POST" accept-charset="utf-8" id="message-form">
<div class="form-group">
<div class="radio">
<label><input name="name2" type="radio" value="name2">Some text 2</label>
</div>
</div>
</form>
</div>
</body>

The problem remains when I use more simple methods like

def test_1
      html.label 'test_1'
end

Im using latest hanami gem from ruby gems.

FormBuilder unexpected values with presenters

I am passing a presenter instance to the form_for helper, which decorates some of the methods of my model (I use plain Sequel, without repositories), and I want the form builder to call that decorated method.

When the form initialized, it calls .symbolize! on the values, which recursively calls itself on the values which respond to .to_hash.

While this behaviour may be handy, it produces unexpected results when we have presenter object which responds to to_hash in the values options.

My solution to the problem was decorating respond_to? :to_hash to return false for the instance of my presenter. While it works, I don't think it is explicit behaviour.

Let me know if you would need any help with this.

Country select helper

country_select is a useful Rails gem to generate a select of countries, with their country codes as values. I'm working on a hanami app right now that could benefit from it.

Can this be ported to Hanami? Note: Rails removed it from the rails project for political reasons: the names/existence of countries can be controversial. Hanami may wish to avoid that too, though going with ISO-3166 seems like a relatively fair option.

I'm happy to either build it into hanami/helpers or make my own gem for it.

Issue with new routing helper

I'm having a problem using the new routing helper, introduced in #14. This is probably a lack in my understanding, if so, feel free to close.

I have the following view:

module Web
  require 'lotus/helpers'

  module Views
    class ApplicationLayout
      include Web::Layout
      include Lotus::Helpers

      def link_to_blog
        %(<a href="#{ routes.blog_path }">Blog</a>)
      end
    end
  end
end

This raises NoMethodError: undefined method 'blog_path'. However, if I change the view to use the path method, it works fine:

module Web
  require 'lotus/helpers'

  module Views
    class ApplicationLayout
      include Web::Layout
      include Lotus::Helpers

      def link_to_blog
        %(<a href="#{ routes.path(:blog) }">Blog</a>)
      end
    end
  end
end

Shouldn't the new routes helper be interchangeable with routes.path?

Markup escape helpers

Introduction

For a design introduction of helpers please have a look at the README.

By including Lotus::Helpers::EscapeHelper it will inject four private methods:

  • #escape_html (aliased as #h)
  • #escape_html_attribute (aliased as #ha)
  • #escape_url (aliased as #u)
  • #raw (which wraps the given argument with Lotus::Utils::Escape::SafeString)

Implementation

This should use Lotus::Utils::Escape facilities, see hanami/utils#51

titleize for label produces capitalized words in multi-word symbol or string

As I've stated in the name I get capitalized words in multi-word symbol or string in a form.

Example: :test_case produces Test Case in the view.
I'd like it to be Test case

In the form_builder.rb the label method uses the titleize method on the content which gives the result above.

Is there a way to work around this?

I'm thinking of something like:

def convert(symbol)
    symbol = symbol.to_s
    if symbol.include?("_")
        symbol.sub!("_", " ").capitalize
    else
        symbol.capitalize
    end
end

It would work also for strings.
regards,
seba

form_for helper giving an undefined method 'params' in tests

Hi,

So.. I'm trying to make a really simple form:

# in the view
def creation_form
  Form.new(:thing, '#', thing: thing) # thing being exposed by the action
end

def creation_form_submit_text
  'Create'
end
-# and the template (in haml)
= form_for creation_form, class: '' do
  - text_field :title
  - submit creation_form_submit_text

With a really small test on the view/template:

require 'spec_helper'
require_relative '../../../../apps/web/views/things/new'

describe Web::Views::Things::New do
  let(:exposures) { Hash[thing: Thing.new] }
  let(:template)  { Hanami::View::Template.new('apps/web/templates/things/new.html.haml') }
  let(:view)      { Web::Views::Things::New.new(template, exposures) }
  let(:rendered)  { view.render }

  it('renders') { rendered }
end

And everything should be shining in glory to the Thing! And it is! At least in my browser, yay!

But it's not in my test...

NoMethodError: undefined method `params' for #<Hanami::View::Rendering::Scope:0x007ff49c85a9b0>

Full error stack: http://0bin.net/paste/qIW55zzj6-TmTtCR#gm-ZUExIHvX8PhTJi0bZMPTHPUI0qN3hu/KMLSsyjv/
Full Gemfile.lock: http://0bin.net/paste/2RiASve4iefyZ+Co#UrbYcXYSbimlQwyRqba1vviD93uD+ql7KWx30cJ6mrE

NoMethodError: undefined method `count' for nil:NilClass when using fields_for_collection

Hi,

I'm trying to use fields_for_collection but I get a strange error. I can recreate the error in a newly generated project when using the example from http://hanamirb.org/guides/1.0/helpers/forms/ (I just removed the non-existing route):

<%=
  form_for :delivery, '#' do
    text_field :customer_name

    fields_for_collection :addresses do
      text_field :street
    end

  submit 'Create'
end
%>

This is what I get on my browser:

Boot Error

Something went wrong while loading /home/malinas/hanami_error_form/config.ru
NoMethodError: undefined method `count' for nil:NilClass

/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/form_builder.rb:246:in `fields_for_collection'
/home/malinas/hanami_error_form/apps/web/templates/home/index.html.erb:7:in `block (2 levels) in singleton class'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:364:in `instance_exec'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:364:in `resolve'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_node.rb:58:in `content'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/html_node.rb:49:in `content'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_node.rb:44:in `to_s'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:332:in `map'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:332:in `to_s'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/form_builder.rb:140:in `to_s'
/home/malinas//hanami_error_form/apps/web/templates/home/index.html.erb:13:in `block in singleton class'
/home/malinas//hanami_error_form/apps/web/templates/home/index.html.erb:-6:in `instance_eval'
/home/malinas//hanami_error_form/apps/web/templates/home/index.html.erb:-6:in `singleton class'
/home/malinas//hanami_error_form/apps/web/templates/home/index.html.erb:-9:in `__tilt_37890300'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/tilt-2.0.8/lib/tilt/template.rb:170:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/tilt-2.0.8/lib/tilt/template.rb:170:in `evaluate'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/tilt-2.0.8/lib/tilt/template.rb:109:in `render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-view-1.0.1/lib/hanami/view/template.rb:41:in `render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-view-1.0.1/lib/hanami/view/rendering.rb:139:in `rendered'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-view-1.0.1/lib/hanami/view/rendering.rb:153:in `layout'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-view-1.0.1/lib/hanami/view/rendering.rb:107:in `render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-view-1.0.1/lib/hanami/view/rendering.rb:258:in `render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:56:in `_render_action'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:48:in `_render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:38:in `render'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/application.rb:169:in `call'
/home/malinas/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/delegate.rb:83:in `method_missing'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/http_router-0.11.2/lib/http_router.rb:193:in `process_destination_path'
(eval):15:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/http_router-0.11.2/lib/http_router.rb:288:in `raw_call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-router-1.0.1/lib/hanami/routing/http_router.rb:156:in `raw_call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/http_router-0.11.2/lib/http_router.rb:142:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-router-1.0.1/lib/hanami/router.rb:1019:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/method_override.rb:22:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/assets/static.rb:49:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/content_length.rb:15:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/common_logger.rb:33:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/builder.rb:153:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/app.rb:42:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/shotgun-0.9.2/lib/shotgun/loader.rb:86:in `proceed_as_child'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/shotgun-0.9.2/lib/shotgun/loader.rb:31:in `call!'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/shotgun-0.9.2/lib/shotgun/loader.rb:18:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/hanami-1.0.0/lib/hanami/assets/static.rb:49:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/lint.rb:49:in `_call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/lint.rb:37:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/show_exceptions.rb:23:in `call'
/home/malinas/.rvm/gems/ruby-2.4.2@temp/gems/rack-2.0.3/lib/rack/handler/webrick.rb:86:in `service'
/home/malinas/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/webrick/httpserver.rb:140:in `service'
/home/malinas/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/webrick/httpserver.rb:96:in `run'
/home/malinas/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/webrick/server.rb:290:in `block in start_thread'

Beyond that error, I'm puzzled by how to use it, because I don't see any hint of an iterator or of a loop in that example. It would really be helpful to have an example that produces an array with two items (unless I misunderstood what this helper was for).

Access non-editable fields in form with `fields_for_collection`

In my application I'm building a big form that edits multiple records at once. I'm doing this by building an html table where each row corresponds to a record. Some of the attributes of my records are not supposed to be editable and should just be displayed (e.g. id).

It seems that there is no official solution at the moment to access and display that attribute. In my code I have to resort to using _value which is part of the private API to achieve this.

Here's an example:

= form_for :thing, routes.thing_path(thing.id), method: :patch, values: {thing: thing} do
  - table class: 'table table-striped' do
    - tbody do
      - fields_for_collection :sub_entities do
        - tr do
          - td do
            - text(_value(:id))
          - td do
            - text_field(:attribute)

Is there a better solution or should I do it this way?

Placeholder option tag in select gets selected by default

With regards to hanami-helpers 1.1.0, the following code:

require 'hanami-helpers'

include Hanami::Helpers

def params
  {}
end

h = html do
  form_for :test, '/test' do
    values = { 'Italy' => 'it',
               '---' => '' }
    select :country, values
  end
end

puts h

produces:

<form action="/test" method="POST" accept-charset="utf-8" id="test-form">
    <select name="test[country]" id="test-country">
        <option value="it">Italy</option>
        <option value="" selected="selected">---</option>
    </select>
</form>

The placeholder is marked as selected when it should not. This also happens with a value of nil (i.e. '---' => nil).

Support for text nodes

I was making my way towards building a form using the HTML5 API. However, it seems to be that it's currently impossible to accomplish the following:

<p>
  <label>
    Your name?
    <input type="text" name="object[name]" />
  </label>
</p>

A possible solution could be something like:

# ...
p do
  label do
    text "Your name?"
    text_field :name
  end
end

What do you guys think?

Cannot interleave Slim and Form Helpers

Hi,

It is currently impossible to mix Slim and Form Helpers in a natural way. For instance, an abridged version of the following bootstrap form does not render correctly if represented in this way:

= form_for 'test', '/' do
  .form-group.row
    label.col-sm-2.col-form-label
    .col-sm-10
      = email_field :email, class: 'form-control', placeholder: 'Email'
  fieldset.form-group
    .row
      legend.col-form-legend.col-sm-2 Radios
      .col-sm-10
        .form-check
          label.form-check-label
            = radio_button :gridradio, 'option1'
            | Option one is this and that—be sure to include why it's great
        .form-check
          label.form-check-label
            = radio_button :gridradio, 'option2'
            | Option two can be something else and selecting it will deselect option one

At the moment, the only way to solve this is to either create every node from the Html Helpers, or to create the form manually.

`submit` helper does not accept block syntax

The submit form helper does not seem to accept the block syntax for nesting tags within. Example:

            = form_for :run, routes.unlock_run_path(run_id), method: :post do
              - submit nil, class: 'btn btn-outline-success' do
                - span class: 'oi oi-check'

leads to:

<form action="/runs/1/unlock" method="POST" accept-charset="utf-8" id="run-form">
<input name="_csrf_token" value="..." type="hidden">
<button type="submit" class="btn btn-outline-success"></button>
</form>

Whereas the button helper reacts differently (note the nested span in the html):

            = form_for :run, routes.lock_run_path(run_id), method: :post do
              - button nil, class: 'btn btn-outline-success' do
                - span class: 'oi oi-check'

yields:

<form action="/runs/3/lock" method="POST" accept-charset="utf-8" id="run-form">
<input name="_csrf_token" value="..." type="hidden">
<button class="btn btn-outline-success">
<span class="oi oi-check"></span>
</button>
</form>

Is this by design or a bug?

Params break when `check_box` is used within `fields_for_collection`

When using check_box within a fields_for_collection block, the params hash can get screwed up. Here's a minimal example:

<%=
  sub_entity1 = OpenStruct.new(key: false, id: 1)
  sub_entity2 = OpenStruct.new(key: false, id: 2)

  thing = OpenStruct.new(sub_entities: [sub_entity1, sub_entity2])

  form_for :thing, '/books/1', method: :put, values: {thing: thing} do
    fields_for_collection :sub_entities do
      hidden_field :id
      check_box :key, name: _displayed_input_name(:key) # workaround due to https://github.com/hanami/helpers/issues/116
    end
    submit 'Save'
  end
%>

When submitting the form with both checkboxes unchecked, the request data looks like this:

Name: Value
_method: PUT
thing[sub_entities][][id]: 1
thing[sub_entities][][key]: 0
thing[sub_entities][][id]: 2
thing[sub_entities][][key]: 0

Which results in the following params on the server:

{
    :_method => "PUT",
      :thing => {
        :sub_entities => [
            {
                 "id" => "1",
                "key" => "0"
            },
            {
                 "id" => "2",
                "key" => "0"
            }
        ]
    },
         :id => "1"
}

That looks fine.

However, if you check one of the checkboxes and submit, the request data looks like this:

Name: Value
_method: PUT
thing[sub_entities][][id]: 1
thing[sub_entities][][key]: 0
thing[sub_entities][][key]: 1
thing[sub_entities][][id]: 2
thing[sub_entities][][key]: 0

Note the double thing[sub_entities][][key] because of the hidden field generated by check_box. Thus, after being parsed by Rack, the params hash is screwed up:

{
    :_method => "PUT",
      :thing => {
        :sub_entities => [
            {
                 "id" => "1",
                "key" => "0"
            },
            {
                "key" => "1",
                 "id" => "2"
            },
            {
                "key" => "0"
            }
        ]
    },
         :id => "1"
}

Expected resulting params hash would be:

{
    :_method => "PUT",
      :thing => {
        :sub_entities => [
            {
                 "id" => "1",
                "key" => "1"
            },
            {
                 "id" => "2",
                "key" => "0"
            }
        ]
    },
         :id => "1"
}

Errors with calling link_to helper in html builder

When I call link_to helper in html helper I have a problems.
I don't know solution for this problem, but I have a patch with test cases:

--- a/test/fixtures.rb
+++ b/test/fixtures.rb
@@ -569,3 +569,22 @@ def link_to_with_content_html_content_id_and_class
   end

 end
+
+class HtmlAndLinkTo
+  include Hanami::Helpers::HtmlHelper
+  include Hanami::Helpers::LinkToHelper
+
+  def two_links_to_in_div
+    html.div do
+      link_to('Posts', 'posts')
+      link_to('Comments', 'comments')
+    end
+  end
+
+  def span_and_link_to_in_div
+    html.div do
+      span 'hello'
+      link_to('Comments', 'comments')
+    end
+  end
+end
diff --git a/test/html_helper_test.rb b/test/html_helper_test.rb
index 1fd6d6f..6487a43 100644
--- a/test/html_helper_test.rb
+++ b/test/html_helper_test.rb
@@ -92,4 +92,18 @@
   it 'autoescapes nested blocks' do
     @view.evil_nested_block_content.to_s.must_equal %(<div>\n<p>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</p>\n</div>)
   end
+
+  describe 'with link_to helper' do
+    before do
+      @view = HtmlAndLinkTo.new
+    end
+
+    it 'returns two links in div' do
+      @view.two_links_to_in_div.to_s.must_equal %(<div>\n<a href=\"comments\">Comments</a>\n<a href=\"posts\">Posts</a>\n</div>)
+    end
+
+    it 'returns span and link in div' do
+      @view.span_and_link_to_in_div.to_s.must_equal %(<div>\n<span>hello</span>\n<a href=\"posts\">Posts</a>\n</div>)
+    end
+  end
 end

No escaping of special characters in attribute values of HTML tags

Hi,

If I use the div helper like this in my Slim template:

= div 'data-story': 'My "own" story'

The resulting HTML is:

<div data-story="My "own" story">

I can also reproduce that in IRB. I expected that the strings would automatically be escaped. Did I miss something? I think that having to do it by myself would defeat the point of using a helper in the first place.

`text_area` method is not providing either `id` or `name` attributes

The title is self-explanatory but here's my own example:

Code

form_for :event, "/events", class: "row" do
  text_area :notes, placeholder: "Some text here.", class: "form-control", rows: 6
end

Output

<form action="/events" method="post" class="row" id="event-form">
  <textarea rows="6" class="form-control" placeholder="Some text here"></textarea>
</form>

It is expected that the textarea has:

  • A name attribute with the value of event[notes];
  • An id attribute with the value of event-note.

EDIT: The root cause of the bug is the inability to store both name and id when no content is passed in. Check the discourse

Some helper methods produce wrong `name` attributes when called in `fields_for_collection` block

Some helper methods return wrong name attributes when called from within a fields_for_collection block. Here's a minimal example:

<%=
  sub_entity1 = OpenStruct.new(key1: 'a1', key2: true, other_key1: 'c1', other_key2: 'd1', id: 1)
  sub_entity2 = OpenStruct.new(key1: 'a2', key2: true, other_key1: 'c2', other_key2: 'd2', id: 2)

  thing = OpenStruct.new(sub_entities: [sub_entity1, sub_entity2])

  form_for :thing, '/books/1', method: :put, values: {thing: thing} do
    fields_for_collection :sub_entities do
      hidden_field :id
      text_area :key1
      check_box :key2
      text_area :other_key1
      text_field :other_key1
      select :other_key2, [['d1', 'd1'], ['d2', 'd2']]
    end
    submit 'Save'
  end
%>

This produces:

<form action="/books/1" method="POST" accept-charset="utf-8" id="thing-form">
  <input type="hidden" name="_method" value="PUT">
  <input type="hidden" name="thing[sub_entities][][id]" id="thing-sub-entities-0-id" value="1">
  <textarea type="textarea" name="thing[sub_entities][0][key1]" id="thing-sub-entities-0-key1" value="a1">a1</textarea>
  <input type="hidden" name="thing[sub_entities][0][key2]" value="0">
  <input type="checkbox" name="thing[sub_entities][0][key2]" id="thing-sub-entities-0-key2" value="1" checked="checked">
  <textarea type="textarea" name="thing[sub_entities][0][other_key1]" id="thing-sub-entities-0-other-key1" value="c1">c1</textarea>
  <input type="text" name="thing[sub_entities][][other_key1]" id="thing-sub-entities-0-other-key1" value="c1">
  <select name="thing[sub_entities][0][other_key2]" id="thing-sub-entities-0-other-key2">
    <option value="d1" selected="selected">d1</option>
    <option value="d2">d2</option>
  </select>
  <input type="hidden" name="thing[sub_entities][][id]" id="thing-sub-entities-1-id" value="2">
  <textarea type="textarea" name="thing[sub_entities][1][key1]" id="thing-sub-entities-1-key1" value="a2">a2</textarea>
  <input type="hidden" name="thing[sub_entities][1][key2]" value="0">
  <input type="checkbox" name="thing[sub_entities][1][key2]" id="thing-sub-entities-1-key2" value="1" checked="checked">
  <textarea type="textarea" name="thing[sub_entities][1][other_key1]" id="thing-sub-entities-1-other-key1" value="c2">c2</textarea>
  <input type="text" name="thing[sub_entities][][other_key1]" id="thing-sub-entities-1-other-key1" value="c2">
  <select name="thing[sub_entities][1][other_key2]" id="thing-sub-entities-1-other-key2">
    <option value="d1">d1</option>
    <option value="d2" selected="selected">d2</option>
  </select>
  <button type="submit">Save</button>
</form>

Note how the name attribute for the hidden_field and the text_field are ok (e.g. thing[sub_entities][][id]). But they're wrong for at least text_area, check_box and select (haven't looked at the others): thing[sub_entities][1][key1].

Apparently _attributes which calls _displayed_input_name does not get called for all helpers.

link_to helper

A common pattern in views is to link to a route, eg:

link_to :blogs_path

Should lotus have a helper method for this? I haven't put much though into the specifics yet.

Add a number_field helper

So that my bookshelf application can say:

div class: 'input' do
  label :percent_read
  number_field :percent_read
end

And I can show off how far I am through various books.

Alternatively, some extra sugar would be nice:

div class: 'input' do
  label :percent_read
  number_field :percent_read, in: 0..100, step: 5
end

So that I can easily update how far I am in the book using 5% increments, but I can't go over 100% (extra credit?) or under 0% (lots and lots of slacking).

Params are present in form builder on AJAX success

In our Hanami project we've run into an issue when we use AJAX and the request is a success. If we want to re-render a section of the page without reloading or redirecting, any form that we attempt to re-render defaults to using the params for the values. At that point however, those params are outdated, and we don't want them to populate the form fields. This is particularly a problem when the params contain a collection, as the form builder is not well equipped to deal with them (it breaks when it attempts to use dig).

We've talked around it a bit and come up with a couple of potential solutions, though we're not 100% happy with either of them. Thought I'd open it as an issue and get some feedback, as one solution involves editing the form builder.

The first option is to have something like this in any view with a :js format:

def params
  return @_params if @_params
  
  @_params = locals[:params].dup
  @_params.to_h.keys.each{ |k| @_params.to_h.delete(k) }
  @_params
end

Using this method we get to retain the params object whilst emptying the part of it which the template actually uses.

The second method we played around with was to essentially add a params: false option to the form_for method. A very rough idea of how this might look would be something like adding this line to the method:

form_params = opts.fetch(:params, self.params)

Then passing form_params into the FormBuilder initializer:

FormBuilder.new(form, attributes, self, form_params, &blk)

Finally, in the FormBuilder initialize method, add form_params as and argument and change the following line:

@values      = Values.new(form.values, @context.params)

to:

@values      = Values.new(form.values, form_params)

Would love to hear some feedback on this. Thanks.

integration/router_helper_test.rb failing

1) Failure:
Routing helper#test_0001_uses helper [/Users/pascal/repositories/lotus/lotus-helpers/test/integration/routing_helper_test.rb:9]:
--- expected
+++ actual
@@ -1 +1,2 @@
-"/dashboard"
+"/dashboard\r
+"

Confirmed on Vagrant using ubuntu 14.04 and ruby 2.2
Also on OS X 10.10.4, Ruby 2.2.0 by @pascalbetz

CI seems fine

Number formatting precision

Hi all,

It looks like NumberFormattingHelper does not fully take into account the given precision:

format_number 3.14, precision: 0
# => 3.0 (instead of 3)
format_number 3.14, precision: 4
# => 3.14 (instead of 3.1400)

Is this by design?

Link_to helper for delete request

I think that it'll be really cool to create helper for delete request. For example link_to_delete Method which generate form with delete request. What do you think it'll be helpful?

FormBuilder output doesn't result in minified HTML

Here's a snippet from the FormBuilder code I have on a site I was building:

= form_for :applicant, routes.employ_path do
    - label 'Contact Name', for: :name, class: 'label login-label'
    - p class: 'control' do
      - text_field :name, class: 'input'
    - label 'Your LinkedIn', for: :linkedin, class: 'label login-label'
    - p class: 'control' do
      - text_field :linkedin, class: 'input'
    - label 'Your GitHub', for: :github, class: 'label login-label'

As you can see below, the rest of the document is very neatly compressed into one line in production except for the FormBuilder HTML:

doesnt-minify

Why is this happening and is this something that is PR-worthy to address?

form_for with only a form object raises an error

Example in docs shows how form_for can be called with a form object.

However, method signature looks like this:

def form_for(name, url, options = {}, &blk)

Note that it has two mandatory positional arguments. This makes it impossible to call the method with only a form object (i.e. without options).

Here's the exception when form_for is called only with a form object (form_for form_object do):

ArgumentError: wrong number of arguments (given 1, expected 2..3)

[...]/hanami-helpers-1.1.1/lib/hanami/helpers/form_helper.rb:409:in `form_for'

Number formatting

Introduce a module that provides formatting facilities for numbers.
It should accept any kind of number and return a comma delimited string, with decimal separator.

Please consider to include both unit and integration tests. Please have a look at lotus-assets for integration tests example.

The goal is to provide private methods (if possible). This should forbid bad usage of those helpers:

<%= format_number cart.total %>

The aimed style is to let templates to bind only one expression.

<%= total %>

Where #total is defined like this:

class MyView
  include Lotus::Helpers::Numbers

  def initialize(cart)
    @cart = cart
  end

  def total
    format_number @cart.total
  end
end

FormBuilder not filling the input fields

Given a form with passed values:

<%= form_for :entity, routes.entities_path, values: { entity: Entity.new(field: 1) }
text_field :field
end %>

The text_field will not be filled at all. I tracked the problem down to the Values#_get_from_values message.

Now that entities are implicitly serializable to hashes (as they respond to to_hash), they become hashes during the Values#initialize and therefore the public_send(k) will always return nil.

Add form helper

Use case: a way to set form action to PATCH or PUT for RESTful update action

ruby 3 support on 1.3.3

Hi, I noticed the ruby 3 support is in main, and soon following a new commit to upgrade util's version to 2.
am wondering is it ok to lock to ref at ruby 3 support commit, and carry on using with [email protected] ?

JRuby ConcurrencyError with tag block form

In my code I'm trying to use the block form of tag, something like this:

  form_for :thing, '/', method: :post do
    tag :label do
      check_box :attribute, value: 'foo'
      text 'bar'
    end
  end

When I try to render this with JRuby, I get a ConcurrencyError:

ConcurrencyError: Detected invalid array contents due to unsynchronized modifications with concurrent users
	org/jruby/RubyArray.java:2486:in `map'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:332:in `to_s'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/form_builder.rb:140:in `to_s'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_node.rb:61:in `content'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/html_node.rb:49:in `content'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_node.rb:44:in `to_s'
	org/jruby/RubyArray.java:2486:in `map'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/html_helper/html_builder.rb:332:in `to_s'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-helpers-1.0.0/lib/hanami/helpers/form_helper/form_builder.rb:140:in `to_s'
	/Users/ferdi/projects/bookshelf/apps/web/templates/books/edit.html.erb:1:in `block in singleton class'
	org/jruby/RubyBasicObject.java:1691:in `instance_eval'
	/Users/ferdi/projects/bookshelf/apps/web/templates/books/edit.html.erb:1:in `singleton class'
	/Users/ferdi/projects/bookshelf/apps/web/templates/books/edit.html.erb:1:in `__tilt_2048'
	org/jruby/RubyMethod.java:115:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/tilt-2.0.8/lib/tilt/template.rb:170:in `evaluate'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/tilt-2.0.8/lib/tilt/template.rb:109:in `render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-view-1.0.0/lib/hanami/view/template.rb:41:in `render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-view-1.0.0/lib/hanami/view/rendering.rb:139:in `rendered'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-view-1.0.0/lib/hanami/view/rendering.rb:153:in `layout'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-view-1.0.0/lib/hanami/view/rendering.rb:107:in `render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-view-1.0.0/lib/hanami/view/rendering.rb:258:in `render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:56:in `_render_action'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:48:in `_render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/rendering_policy.rb:38:in `render'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/application.rb:169:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/stdlib/delegate.rb:83:in `method_missing'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/http_router-0.11.2/lib/http_router.rb:193:in `process_destination_path'
	(eval):15:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/http_router-0.11.2/lib/http_router.rb:288:in `raw_call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-router-1.0.1/lib/hanami/routing/http_router.rb:156:in `raw_call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/http_router-0.11.2/lib/http_router.rb:142:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-router-1.0.1/lib/hanami/router.rb:1019:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/method_override.rb:22:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/assets/static.rb:49:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/content_length.rb:15:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/common_logger.rb:33:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/builder.rb:153:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/app.rb:42:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/hanami-1.0.0/lib/hanami/assets/static.rb:49:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/lint.rb:49:in `_call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/lint.rb:37:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/show_exceptions.rb:23:in `call'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/gems/shared/gems/rack-2.0.3/lib/rack/handler/webrick.rb:86:in `service'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/stdlib/webrick/httpserver.rb:140:in `service'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/stdlib/webrick/httpserver.rb:96:in `run'
	/Users/ferdi/.rbenv/versions/jruby-9.1.13.0/lib/ruby/stdlib/webrick/server.rb:296:in `block in start_thread'

The same code does not lead to an exception when using MRI.

It really seems to have to do with the block form of the tag method. The following code works fine:

  form_for :thing, '/', method: :post do
    tag :label
    check_box :attribute, value: 'foo'
    text 'bar'
  end

the selected attribute on the option tag is not set

Hi,

I'm using select helper and the selected attribute on the option tag is not automatically set. For example, when displaying an entity for edit and update.

I've setup a small bookshelf with book and author entities. It's minimalistic just to get the minimum required environment ready.

Hanami data:
hanami, '0.7.2'
hanami-model, '> 0.5'
hanami-helpers (
> 0.3)

Sample app: https://github.com/sebastjan-hribar/hanami-bookshelf

This is the html from inspecting page:

<h2>Edit book</h2>

<form action="/books/1" method="POST" accept-charset="utf-8" id="book-form">
<input type="hidden" name="_method" value="PATCH">
<div class="input">
<label for="book-title">Title</label>
<input type="text" name="book[title]" id="book-title" value="Test Book">
</div>
<div class="input">
<label for="book-author">Author</label>
<select name="book[author]" id="book-author">
<option value="1">Best Author</option>
<option value="2">Second Best Author</option>
</select>
</div>
<div class="controls">
<button type="submit">Update Book</button>
</div>
</form>

I've similar problems in my other app, I've setup this one just for this select problem.

Please let me know if I should add further information.

regards,
Sebastjan

rake test: undefined method `param' for DeliveryParams:Class (NoMethodError)

After forking and w/o any changes yet rake test for helpers gives the following error:

(eval):2: warning: method redefined; discarding old link
(eval):2: warning: previous definition of link was here
/home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/presenter.rb:1: warning: loading in progress, circular require considered harmful - /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/view/escape.rb
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in  `<main>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in  `select'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in  `block in <main>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in  `each'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in  `block (2 levels) in <main>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in  `require'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/escape_helper_test.rb:1:in  `<top (required)>'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/escape_helper_test.rb:1:in  `require'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/test_helper.rb:12:in  `<top (required)>'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/test_helper.rb:12:in  `require_relative'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/fixtures.rb:1:in  `<top (required)>'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/fixtures.rb:1:in  `require'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/view.rb:8:in  `<top (required)>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/view.rb:8:in  `require'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/view/escape.rb:2:in  `<top (required)>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/view/escape.rb:2:in  `require'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/presenter.rb:1:in  `<top (required)>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/bundler/gems/view-6a51e5cd3daf/lib/hanami/presenter.rb:1:in  `require'
/home/sebastjan/.rvm/gems/ruby-2.2.1/gems/dry-types-0.7.2/lib/dry/types/options.rb:18: warning: method redefined; discarding old meta
/home/sebastjan/.rvm/gems/ruby-2.2.1/gems/dry-types-0.7.2/lib/dry/types/struct.rb:62: warning: instance variable @schema not initialized
/home/sebastjan/.rvm/gems/ruby-2.2.1/gems/dry-types-0.7.2/lib/dry/types/struct.rb:56: warning: instance variable @constructor_type not initialized
/home/sebastjan/Dokumenti/projekti/hanami/helpers/test/fixtures.rb:342:in `<class:DeliveryParams>': undefined method `param' for DeliveryParams:Class (NoMethodError)
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/fixtures.rb:341:in `<top (required)>'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/test_helper.rb:12:in `require_relative'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/test_helper.rb:12:in `<top (required)>'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/escape_helper_test.rb:1:in `require'
    from /home/sebastjan/Dokumenti/projekti/hanami/helpers/test/escape_helper_test.rb:1:in `<top (required)>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in `require'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:10:in `block (2 levels) in <main>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in `each'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:9:in `block in <main>'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in `select'
    from /home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb:4:in `<main>'
rake aborted!
Command failed with status (1): [ruby -w -I"lib:test" -I"/home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib" "/home/sebastjan/.rvm/gems/ruby-2.2.1/gems/rake-11.1.2/lib/rake/rake_test_loader.rb" "test/**/*_test.rb" ]
/home/sebastjan/.rvm/gems/ruby-2.2.1/bin/ruby_executable_hooks:15:in `eval'
/home/sebastjan/.rvm/gems/ruby-2.2.1/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => test
(See full trace by running task with --trace)

select: selected option depends on order of options

I just discovered this while upgrading to hanami 1.1.
My tests were failing, claiming the expected option was not selected in a <select> element. The same test passed in hanami 1.0. I dug around a bit. Apparently the fix in c0043de introduced a new bug:

If your options contain a nil option, it must be the last option. Otherwise the rendered selection will not be correct if anything but nil is the value of your attribute.

Example:

In my view I have:

- foo = OpenStruct.new(bar: 'a')

= form_for(:foo, '', values: {foo: foo}) do
  - select :bar, [['a', 'a'], ['b', 'b'], ['nil option', nil]]

= form_for(:foo, '', values: {foo: foo}) do
  - select :bar, [['nil option', nil], ['a', 'a'], ['b', 'b']]

This leads to the HTML:

<form action="" method="POST" accept-charset="utf-8" id="foo-form">
<input name="_csrf_token" value="0ed6c40b814d0f394d6221746609fc6bcd4eed530a6c773ce39ad63260835b95" type="hidden">
<select name="foo[bar]" id="foo-bar">
<option value="a" selected="selected">a</option>
<option value="b">b</option>
<option value="">nil option</option>
</select>
</form>
<form action="" method="POST" accept-charset="utf-8" id="foo-form">
<input name="_csrf_token" value="0ed6c40b814d0f394d6221746609fc6bcd4eed530a6c773ce39ad63260835b95" type="hidden">
<select name="foo[bar]" id="foo-bar">
<option value="" selected="selected">nil option</option>
<option value="a">a</option>
<option value="b">b</option>
</select>
</form>

Which consequently renders as:

20180323_140640

In this example, both selects should show 'a' as the selected option.

Migrate from Minitest to RSpec

A few rules to follow:

  • If you want to work on this, please leave a comment here. We want to avoid many people to work on the same task.

  • We expect this migration to happen within a week from now.

  • Do not change the tests, only migrate from Minitest to RSpec syntax. If not sure, please ask here.

  • Do not remove the old tests, only add the new ones.

  • Make CI build to use RSpec tests.

  • Target develop branch, instead of master.

  • All the tests artefacts like fixtures, goes under spec/support.

  • Try to reflect the structure under lib/. Eg. lib/hanami/view.rb should be tested under spec/hanami/view_spec.rb, while test/rendering_test.rb should go under spec/integration/rendering_spec.rb.

If you want to take inspiration, please have a look at: hanami/mailer#67

`label` helper doesn't accept block syntax

Similar to #127, but with label.

Example use case:

label for: :pizza do
  text 'Pizza '
  abbr '*', title: 'required', 'aria-label': 'required'
end

Which is more accessible than just having label 'Pizza *', for: :pizza 🙂 🍕

The `select` method expects an inverted hash for its `values` parameter

Hi,

When providing values to select, the code expects a hash such as:

{ text: id, text: id,  }

I think that it's a very surprising way of doing things, and actually even the documentation is confused (cf http://www.rubydoc.info/gems/hanami-helpers/Hanami/Helpers/FormHelper/FormBuilder#select-instance_method ):

values = Hash['Italy' => 'it', 'United States' => 'us']

appears in the first two examples, while:

values = Hash['it' => 'Italy', 'us' => 'United States']

appears in the subsequent six ones.

While the easy fix is to update the documentation, I think a breaking change could be considered instead, to have the hashes in the "right" order.

Form helper does not handle numbers for nested fields

Hi,

I ran into a small problem with version 1.0.0. I'm not sure whether it is a bug or me trying to do something I shouldn't. I came up with the following MWE:

require 'hanami-helpers'

include Hanami::Helpers

users = [ { user_id: 10 }, { user_id: 11 } ]

def params; {}; end

r = html.div do
  form_for :users, '#' do
    users.each_with_index do |a, i|
      fields_for i do
        hidden_field :user_id, value: a[:user_id]
        text_area :story
      end
    end
  end
end

puts r.to_s

It produces the following HTML code:

<div>
<form action="#" method="POST" accept-charset="utf-8" id="users-form">
<input type="hidden" name="users[][user_id]" id="users-0-user-id" value="10">
<textarea name="users[0][story]" id="users-0-story"></textarea>
<input type="hidden" name="users[][user_id]" id="users-1-user-id" value="11">
<textarea name="users[1][story]" id="users-1-story"></textarea>
</form>
</div>

As you can see, the name of the input type="hidden" is incomplete, and Rack raises an exception when the form is submitted. I thought that maybe using an Integer caused a problem somehow, so I replaced fields_for i do by fields_for i.to_s do but the result is the same. Only if I append or prepend a non-numeric character do I get to see the expected result.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.