Indentation oops with "if condition then <a> else <span>" in slim - ruby-on-rails

I have to render either an or a depending on a condition, however, slim's indentation and "no explicit end allowed" gets in the way.
In this simplified code, I'd like to wrap a product with three DIVs in either an A or a SPAN depending on whether the product is available or not.
- if available?
a href=...
- else
span class="unavailable"
.currency = #product.currency
.price = #product.price
.description = #product.description
The above obviously won't work since the product is not rendered within the A or SPAN but next to it.
- if available?
a href=...
- else
span class="unavailable"
.currency = #product.currency
.price = #product.price
.description = #product.description
This case is even worse, because slim treats the product as part of the "else" case and renders the product next to the SPAN and not at all for the A.
- if available?
a href=...
- else
span class="unavailable"
.currency = #product.currency
.price = #product.price
.description = #product.description
Here everything is fine for the SPAN but the A remains empty because the product is treated as part of the "else" case.
- if available?
a href=...
- else
span class="unavailable"
- end
.currency = #product.currency
.price = #product.price
.description = #product.description
Now, this could be correct, but slim doesn't allow explicit "end" and therefore raises an exception.
Any idea how I can get this to work?
PS: Maybe slim should allow explicit "end" for edge cases like this one.

This is what I could come up with as a workaround:
Using capturing to local variables you save your children in a local variable:
= capture_to_local children=:children
.currency = #product.currency
.price = #product.price
.description = #product.description
And now you can run
- if available?
a href=...
= children
- else
span class="unavailable"
= children
I don't have slim set up, just tried it with haml, but it should work just the same :)

Related

Rails form mix checkboxes and radio buttons on same parameter

In my form I have :option_ids which passes a list of IDs back to the model to build associations with the applicable Options. In my case there are groups of options allowing the user to select any combination of options.
Currently I have this in my form:
- #option_groups.each do |group|
- unless group.options.blank?
.form
h3 = group.name
.checkboxes
= line_item.collection_check_boxes :option_ids, group.options, :id, :name_and_price do |option|
.checkbox.horizontal
= option.check_box(class: "check")
= image_tag option.object.photo.variant(resize: "65x65") if option.object.photo.attached?
= option.label
Recently we added a condition to the OptionGroup model allowing users to define whether multiple options from this OptionGroup could be selected(checkboxes), or only one(radio buttons).
What I'm wondering is if there's a way I can still populate the :option_ids parameter using a combination of checkboxes and radio buttons. I've tried the following but it messes up the params hash
-#option_groups.each do |group|
- unless group.options.blank?
.form
h3 = group.name
- if group.multiple?
.checkboxes
= line_item.collection_check_boxes :option_ids, group.options, :id, :name_and_price do |option|
.checkbox.horizontal
= option.check_box
= image_tag option.object.photo.variant(resize: "65x65") if option.object.photo.attached?
= option.label
- else
span Please select one
.checkboxes
= line_item.collection_radio_buttons :option_ids, group.options, :id, :name_and_price do |option|
.checkbox.horizontal
= option.radio_button
= image_tag option.object.photo.variant(resize: "65x65") if option.object.photo.attached?
= option.label
Has anyone encountered a scenario like this before? How would someone conditionally impose exclusivity while not messing up the other id's in the form attribute?

if else if else in angular js

I have to incorporate angular in my slim file, I'm not sure how to transform if else block in angular. I know angular don't have ng if else statements but is there a way to change below code to angular
- if #cart['empty']
cart is empty
- elsif #cart['invalid']
can't proceed
- else
-#cart['Items'].each do |item|
#{item['description']}
I want to achieve some thing like this
ng-if="cart.empty"
cart is empty
ng-else-if cart.invalid
can't proceed
ng-else
ng-repeat="cart.Items as item"
item.description
What you're looking for is the ng-switch directive.
First get some variable that will contain the switch condition:
$scope.getStatus = function(cart) {
if (cart.empty) return 'empty';
if (cart.invalid) return 'invalid';
}
And then use the directive:
<div ng-switch = "getStatus(cart)">
<div ng-switch-when = "empty">Cart is empty</div>
<div ng-switch-when = "invalid">Can't proceed</div>
<div ng-switch-default>.... ok ....</div>
</div>

HAML creates unnecessary spaces while using loops

In Rails with HAML, I want to create a simple line of spaces: ====== on the page, with variable length. However, the following code:
- 10.times do
\=
renders the following:
= = = = = = = = = =
I want to get rid of the spaces in the middle of these equals signs, but I don't really have any idea how. I know with ERB you can use
<%= 10.times do -%>=<% end %>
but I'm having trouble figuring out how to do it here.
Another approach, tested, should work:
= "=" * 10
This should work
- 10.times do
&= "="

Surrounding cents in <sup> tag using Rails number_to_currency

How can I isolate cents and place them inside their own element? The output I'm looking for is this:
<sup>$</sup>20<sup>99</sup>
Notice there is no delimiter to separe the decimal units, and they're contained in their own sup tag. I know how to get <sup>$</sup>20.99 when using format: '<sup>%u</sup>%n', but this does not give me a way to isolate cents.
Any ideas?
You are going to have to do it with substitution regex or something similar.
20.99.number_to_currency.sub(/\^([^\d]+)(\d+)([^\d]+)(\d+)/,
'\1<sup>\2</sup>\3<sup>\4</sup>')
I personally use this method, it allow me to support I18n properly but also to only use the <sub> container when I want the number displayed in HTML.
def formated_price(price, currency, options = {})
html = options[:html].nil? ? false : options[:html]
money = number_to_currency(price, unit: currency) || h('')
if html
separator = I18n.t('number.currency.format.separator')
tmp = money.split(separator)
tmp[1] = tmp[1].sub(/\A(\d+)(.*)\z/, content_tag(:sup, separator + '\1') + '\2') if tmp[1]
money = tmp.join.html_safe
end
money
end
if you like your currency unit to be in <sup> as well when using HTML, you could use this instead:
def formated_price(price, currency, options = {})
html = options[:html].nil? ? false : options[:html]
if html
money = number_to_currency(price, unit: content_tag(:sup, currency)) || h('')
separator = I18n.t('number.currency.format.separator')
tmp = money.split(separator)
tmp[1] = tmp[1].sub(/\A(\d+)(.*)\z/, content_tag(:sup, separator + '\1') + '\2') if tmp[1]
money = tmp.join.html_safe
else
number_to_currency(price, unit: currency) || h('')
end
end
If you find any issue, please let me know.

Split #blogs into three divs using size of description field as weight

I have a collection of Blog items.
#blogs = Blog.find(:all)
Each blog has a description textfield with some text. What I would like to do is splitting the #blogs objects into 3 divs, but with roughly the same characters in each column.
<div id="left">
#blog1 (653 characters)
</div>
<div id="center">
#blog2 (200 characters)
#blog5 (451 characters)
</div>
<div id="right">
#blog3 (157 characters)
#blog4 (358 characters)
#blog6 (155 characters)
</div>
I can't figure out how to do that without getting really complicated and probably inefficient.
So far I have thought about converting the description field (size) to % of total characters in the #blogs collection, but how do I match/split the elements, so that I get closest to 33% in each column - like a super simple tetris game :)
Any thoughts?
Here's a quick hack that isn't perfect, but might get you pretty close. The algorithm is simple:
Sort items by size.
Partition items into N bins.
Resort each bin by date (or other field, per your desired presentation order)
Here's a quick proof of concept:
#!/usr/bin/env ruby
# mock out some simple Blog class for this example
class Blog
attr_accessor :size, :date
def initialize
#size = rand(700) + 100
#date = Time.now + rand(1000)
end
end
# create some mocked data for this example
#blogs = Array.new(10) { Blog.new }
# sort by size
sorted = #blogs.sort_by { |b| b.size }
# bin into NumBins
NumBins = 3
bins = Array.new(NumBins) { Array.new }
#blogs.each_slice(NumBins) do |b|
b.each_with_index { |x,i| bins[i] << x }
end
# sort each bin by date
bins.each do |bloglist|
bloglist.sort_by! { |b| b.date }
end
# output
bins.each_with_index do |bloglist,column|
puts
puts "Column Number: #{column+1}"
bloglist.each do |b|
puts "Blog: Size = #{b.size}, Date = #{b.date}"
end
total = bloglist.inject(0) { |sum,b| sum + b.size }
puts "TOTAL SIZE: #{total}"
end
For more ideas, look up the multiprocessor scheduling problem.

Resources