String interpolation in form inputs - ruby

In Rails, I'm trying to create video uploader forms for multiple video types.
I ended with code like this:
= semantic_form_for element, url: form_url, method: form_method, remote: true do |f|
// there will be some form should return once
- ['webm', 'mp4', 'ogv'].each do |ext|
.video-item-uploader
= f.input :"#{ext}", hint: [ f.object."#{ext}"? ? "#{I18n.t('uploaded')}" : '' ].join.html_safe
= f.input :"#{ext + '_cache'}", as: :hidden
= f.input :_destroy, as: :boolean, label: "#{I18n.t('do_delete')}"
With code like that, I got syntax errors. It works if I replace "#{ext}" with any text (without quotes) from array.
Is there any possibility to insert a variable inside a form input like that?

= semantic_form_for element, url: form_url, method: form_method, remote: true do |f|
// there will be some form should return once
- %w(webm mp4 ogv).each do |ext|
.video-item-uploader
= f.input :"#{ext}", hint: [ f.object.send("#{ext}?")? ? "#{I18n.t('uploaded')}" : '' ].join.html_safe
= f.input :"#{ext + '_cache'}", as: :hidden
= f.input :_destroy, as: :boolean, label: "#{I18n.t('do_delete')}"
try this

Related

How to use Simple Form and Direct Upload ActiveStorage

I'm having a trouble when I try to use Simple Form gem for upload video. I'm using ActiveStorage and local storage for this.
My form looks like this:
= simple_form_for #film do |f|
= f.error_notification
= f.input :title, as: :string
= f.input :description, as: :string
= f.input :cover_img, as: :file
= f.input :film_link, as: :file, direct_upload: true
= f.button :submit
= link_to 'back', :back, class: 'btn btn-secondary'
I've followed instruction on here https://edgeguides.rubyonrails.org/active_storage_overview.html
So I've included js and css files in my app. But that won't work. It looks like there's some troubles with passing direct_upload: true via simple_form.
I've also find article https://phase2online.com/blog/2018/10/03/easily-upload-files-with-active-storage-in-rails-5-2/ and pull git repo from here
An this works on when you'll use form_for on your _form. When I change form to use simple_form gem(instead of form_form use simple_form_for) it won't work.
Anyone have an idea why this is not working please?
I use ruby 2.6.3 and Rails 5.2.3 and simple_form (5.0.1)
There're is the way to make this work.
We can change f.input to f.file_field as hashrocket suggest - but then validation of simple form will not work, and perhaps we have to add class to this input plus extra div before.
We can add html attribute to the f.input.
For me this is working I hope it will help someone else too.
= simple_form_for #film do |f|
= f.error_notification
= f.input :title, as: :string
= f.input :description, as: :string
= f.input :cover_img, as: :file, input_html: { data: { direct_upload_url: '/rails/active_storage/direct_uploads' } }
= f.input :film_link, as: :file, input_html: { data: { direct_upload_url: '/rails/active_storage/direct_uploads' } }
= f.button :submit
= link_to 'back', :back, class: 'btn btn-secondary'
These days (rails 6.x), you shouldn't need to specify as: file (simple form should figure this out via reflection) and you can just pass direct_upload: true to input_html:
= simple_form_for #film do |f|
= f.error_notification
= f.input :title, as: :string
= f.input :description, as: :string
= f.input :cover_img, input_html: { direct_upload: true }
= f.input :film_link, input_html: { direct_upload: true }
= f.button :submit
= link_to 'back', :back, class: 'btn btn-secondary'
Verify it's doing the right thing by looking at the generated html. You should see an attribute like this on each input which is of type="file":
data-direct-upload-url="https://<your app server>/rails/active_storage/direct_uploads"
#anka's suggestion to create a custom input type is a good one, but you could also edit the default simple form wrappers for file types if you know all your files will be direct uploads to S3.
I would suggest to simply add a custom input for that to avoid repetition within your view files. Put this in the file app/inputs/direct_upload_file_input.rb:
# frozen_string_literal: true
class DirectUploadFileInput < SimpleForm::Inputs::FileInput
def input_html_options
super.merge({ direct_upload: true })
end
end

Export data to a .csv file on ruby app

I got an error
undefined method `form_for_results_path'
in my code to Export the data to a .csv file. Really don't know if I missed something. Here what I have:
item_controller
def export
CSV.open("result_data.csv", "w") do |csv|
csv << ["cod", "genre", "den_cont"]
end
end
In the view
<%= link_to 'Download CSV', form_for_results_path(#form), :method => :export %>
Thanks
Change it to this and read some more about rails basics.
<%= link_to 'Download CSV', controller: :item, action: :export%>
in your controller, you should do something like this, using rails send_data method:
def export
data = # your data in csv format
options = {
:filename => "some_name.csv",
:type => 'text/csv'
}
send_data(data, options)
end
you can also change your download link using a route_helper. add an entry in your routes file
get 'items/export', :as 'download_items_data"
then your link_to can look like this:
link_to 'Download CSV', download_items_data_path

Rails 4.0.2 haml version of form_for drive me crazy

I am quite new to Rails world and I am trying to create a form for my ticket model
I have this following in my routes.rb
resources :tickets
In my tickets_controller, I have the following
def new
#ticket = Ticket.new
end
And in my tickets/new.html.haml
= form_for(#ticket, :url => { :action => "create" }) do |f| # <- this line is causing error
...
My error is syntax error, unexpected ')'
I don't understand cause similar template file is working. Also, if I try to write the following tickets/new.html.erb, it is working
<%= form_for(:ticket, :url => { :action => "create" }) do |f| %>
<% end %>
Could you please help me to understand what i am exactly doing wrong ?
Thank you very much !
I found your problem, one was related to indentation(if-block) and other was an extra comma(submit button).
%h1{:class => ["page-header"]} New Ticket
= form_for(#ticket, :url => { :action => "create" }) do |f|
= if #ticket.errors.any?
%h2 #{pluralize(#ticket.errors.count, "error")} + " prohibited this post from being saved:" ### code indentation done as it belongs to if block
%ul
= #ticket.errors.full_messages.each do |msg|
%li = msg
%p
= f.label :subject
= f.text_field :subject
%p
= f.label :body
= f.text_area :body
%p
= f.submit :class => "btn btn-primary" ### Removed comma
= link_to 'Back', tickets_path

Configure the label of Active Admin has_many

Well I have a two models related with a on-to-many assoc.
#models/outline.rb
class Outline < ActiveRecord::Base
has_many :documents
end
#models/document.rb
class Document < ActiveRecord::Base
belongs_to :outline
end
#admin/outlines.rb
ActiveAdmin.register Outline do
form do |f|
f.inputs "Details" do
f.input :name, :required => true
f.input :pages, :required => true
...
f.buttons
end
f.inputs "Document Versions" do
f.has_many :documents, :name => "Document Versions" do |d|
d.input :file, :as => :file
d.buttons do
d.commit_button :title => "Add new Document Version"
end
end
end
end
end
Well as you can see in the admin/outlines.rb I already tried setting up the :name, in the has_many :documents, and the :title in the commit_button, but neither of that options work, I also tried with :legend, :title, and :label, instead of :name in the .has_many. Not working.
This is the result of that code:
Screenshot
What I want to display is "Document Versions" instead of "Documents", and "Add new Document Version" instead of "Add new Document"
If someone can have a solution it would be great
To set has_many header you can use
f.has_many :images, heading: 'My images' do |i|
i.input :src, label: false
end
See here
Looking at ActiveAdmin tests("should translate the association name in header"), there may be another way of doing this. Use your translation file.
If you look at ActiveAdmin has_many method (yuck!!! 46 lines of sequential code), it uses ActiveModel's human method.
Try adding this to your translation file
en:
activerecord:
models:
document:
one: Document Version
other: Document Versions
One quick hack is that you can hide the h3 tag through its style.
assets/stylesheets/active_admin.css.scss
.has_many {
h3 {
display: none;
}}
This will hide any h3 tag under a has_many class.
You can customise the label of the "Add..." button by using the new_record setting on has_many. For the heading label you can use heading:
f.has_many :documents,
heading: "Document Versions",
new_record: "Add new Document Version" do |d|
d.input :file, :as => :file
end
Sjors answer is actually a perfect start to solving the question. I monkeypatched Active Admin in config/initializers/active_admin.rb with the following:
module ActiveAdmin
class FormBuilder < ::Formtastic::FormBuilder
def titled_has_many(association, options = {}, &block)
options = { :for => association }.merge(options)
options[:class] ||= ""
options[:class] << "inputs has_many_fields"
# Set the Header
header = options[:header] || association.to_s
# Add Delete Links
form_block = proc do |has_many_form|
block.call(has_many_form) + if has_many_form.object.new_record?
template.content_tag :li do
template.link_to I18n.t('active_admin.has_many_delete'), "#", :onclick => "$(this).closest('.has_many_fields').remove(); return false;", :class => "button"
end
else
end
end
content = with_new_form_buffer do
template.content_tag :div, :class => "has_many #{association}" do
form_buffers.last << template.content_tag(:h3, header.titlecase) #using header
inputs options, &form_block
# Capture the ADD JS
js = with_new_form_buffer do
inputs_for_nested_attributes :for => [association, object.class.reflect_on_association(association).klass.new],
:class => "inputs has_many_fields",
:for_options => {
:child_index => "NEW_RECORD"
}, &form_block
end
js = template.escape_javascript(js)
js = template.link_to I18n.t('active_admin.has_many_new', :model => association.to_s.singularize.titlecase), "#", :onclick => "$(this).before('#{js}'.replace(/NEW_RECORD/g, new Date().getTime())); return false;", :class => "button"
form_buffers.last << js.html_safe
end
end
form_buffers.last << content.html_safe
end
end
end
Now in my admin file I call titled_has_many just like has_many but I pass in :header to override the use of the Association as the h3 tag.
f.titled_has_many :association, header: "Display this as the H3" do |app_f|
#stuff here
end
Does not deserve a prize but you could put this in config/initializers/active_admin.rb . It will allow you to tweak the headers you want using a config/locales/your_file.yml (you should create the custom_translations entry yourself). Dont forget to restart the server. And use the f.hacked_has_many in your form builder.
module ActiveAdmin
class FormBuilder < ::Formtastic::FormBuilder
def hacked_has_many(association, options = {}, &block)
options = { :for => association }.merge(options)
options[:class] ||= ""
options[:class] << "inputs has_many_fields"
# Add Delete Links
form_block = proc do |has_many_form|
block.call(has_many_form) + if has_many_form.object.new_record?
template.content_tag :li do
template.link_to I18n.t('active_admin.has_many_delete'), "#", :onclick => "$(this).closest('.has_many_fields').remove(); return false;", :class => "button"
end
else
end
end
content = with_new_form_buffer do
template.content_tag :div, :class => "has_many #{association}" do
# form_buffers.last << template.content_tag(:h3, association.to_s.titlecase)
# CHANGED INTO
form_buffers.last << template.content_tag(:h3, I18n.t('custom_translations.'+association.to_s))
inputs options, &form_block
# Capture the ADD JS
js = with_new_form_buffer do
inputs_for_nested_attributes :for => [association, object.class.reflect_on_association(association).klass.new],
:class => "inputs has_many_fields",
:for_options => {
:child_index => "NEW_RECORD"
}, &form_block
end
js = template.escape_javascript(js)
_model = 'activerecord.models.' + association.to_s.singularize
_translated_model = I18n.t(_model)
js = template.link_to I18n.t('active_admin.has_many_new', :model => _translated_model), "#", :onclick => "$(this).before('#{js}'.replace(/NEW_RECORD/g, new Date().getTime())); return false;", :class => "button"
form_buffers.last << js.html_safe
end
end
form_buffers.last << content.html_safe
end
end
end
If you have issues with locale files not being loaded good in staging/production mode, adding this to your application.rb might help (substitute :nl for the right locale)
config.before_configuration do
I18n.load_path += Dir[Rails.root.join('config','locales','*.{rb,yml}').to_s]
I18n.locale = :nl
I18n.default_locale = :nl
config.i18n.load_path += Dir[Rails.root.join('config','locales','*.{rb,yml}').to_s]
config.i18n.locale = :nl
config.i18n.default_locale = :nl
I18n.reload!
config.i18n.reload!
end
config.i18n.locale = :nl
config.i18n.default_locale = :nl

Haml: Append class if condition is true in Haml

If post.published?
.post
/ Post stuff
Otherwise
.post.gray
/ Post stuff
I've implemented this with rails helper and it seems ugly.
= content_tag :div, :class => "post" + (" gray" unless post.published?).to_s do
/ Post stuff
Second variant:
= content_tag :div, :class => "post" + (post.published? ? "" : " gray") do
/ Post stuff
Is there a more simple and haml-specific way?
UPD. Haml-specific, but still not simple:
%div{:class => "post" + (" gray" unless post.published?).to_s}
/ Post stuff
.post{:class => ("gray" unless post.published?)}
- classes = ["post", ("gray" unless post.published?)]
= content_tag :div, class: classes do
/Post stuff
def post_tag post, &block
classes = ["post", ("gray" unless post.published?)]
content_tag :div, class: classes, &block
end
= post_tag post
/Post stuff
Really the best thing is to put it into a helper.
%div{ :class => published_class(post) }
#some_helper.rb
def published_class(post)
"post #{post.published? ? '' : 'gray'}"
end
HAML has a nice built in way to handle this:
.post{class: [!post.published? && "gray"] }
The way that this works is that the conditional gets evaluated and if true, the string gets included in the classes, if not it won't be included.
Updated Ruby syntax:
.post{class: ("gray" unless post.published?)}

Resources