How to Combine many similar methods and views into one - ruby-on-rails-4

I want to combine similar methods and views into one, but still keep the url name, like the following:
Home/recommends/categories/shopping
Home/recommends/categories/nightview
Home/recommends/categories/food
Home/recommends/categories/area
I don't want to use params like "?something=xyz" in url.
In routes.rb:
resources :recommends, only: :index do
collection do
resources :categories, only: :show, controller: 'recommends' do
collection do
get :food
get :area
get :shopping
get :nightview
end
end
end
end
In controllers:
def food
set_paginator
#recommends = UserRecommend.where(category: "food").order('created_at desc').offset(#offset).limit(#limit).all
#number_of_recommends = UserRecommend.where(category: "food").count
end
def area
set_paginator
#recommends = UserRecommend.where(category: "area").order('created_at desc').offset(#offset).limit(#limit).all
#number_of_recommends = UserRecommend.where(category: "area").count
end
...
In views I have:
food.html.slim
area.html.slim
shopping.slim
nightview.slim
Which are using the same code, just different names in h1:
h1
| Shopping ( or Area or Food... )
= " (#{#number_of_recommends})"
= render partial: "layouts/paginator",
locals: { total_items: #number_of_recommends, per_page: #limit, current_page: #page }
= render partial: "table", locals: { recommends: #recommends }
Can anyone help me refactor this code?

You can (and should) have a single route, a single action, and a single view. The key is to make the variable portion of your URL into an actual variable. You do this using dynamic segments.
First, a single route. There is no need to use resources if you're not actually generating multiple RESTful actions:
get "/recommends/categories/:category" => "categories#show"
You can add criteria on what is allowed for the :category segment:
get "/recommends/categories/:category" => "categories#show", category: /food|area|shopping|nightview/
Next, a single action:
class CategoriesController < ApplicationController
before_action :set_paginator
def show
# params[:category] is "food"/"area"/etc
categories = UserRecommend.where(category: params[:category]).order('created_at desc')
#recommends = categories.offset(#offset).limit(#limit)
#number_of_recommends = categories.count
end
end
Finally, a single view:
# app/views/categories/show.slim
h1
= params[:category].capitalize
= " (#{#number_of_recommends})"
= render partial: "layouts/paginator",
locals: { total_items: #number_of_recommends, per_page: #limit, current_page: #page }
= render partial: "table", locals: { recommends: #recommends }
I would consider it better to use localization to turn the params[:category] into a title, which would give you more control, rather than relying on simple capitalization of the URL segment:
# app/views/categories/show.slim
h1
= t params[:category]
And:
# config/locals/en.yml
en:
categories:
show:
food: 'Food'
area: 'Area'
nightview: 'Night View'

Related

How to test controller method? Rails 4

How to test this (with or without RSpec)?
def create
#foo = Model1.find(params[:array_of_ids])
#foo.each do |f|
#boo = Model2.new
#boo.attributes = f.attributes
#boo.save! unless Model2.exists?(#boo.id)
end
end

how to pass 2 data bag variables to template chef

I am trying to pass 2 data bags as variables into a template but it end in error message. Do anyone know how do i pass 2 databags to a template?
Recipe
db = data_bag_item('dbconnect', 'connection')
dbkey = data_bag_item('database', 'databasename')
template '/etc/config.cnf' do
source 'config.cnf.erb'
action :create
variables (
:dbcon => db,
:dbk => dbkey
)
end
Template
connection = mysql://<%= #dbcon['dbuser'] %>:<%= #dbcon['dbpasswd'] %>#<%= #dbcon['dbname'] %>/<%= #dbk['dbname'] %>
Okay. I got the answer.
I missed {} brackets in variables.
db = data_bag_item('dbconnect', 'connection')
dbkey = data_bag_item('database', 'databasename')
template '/etc/config.cnf' do
source 'config.cnf.erb'
action :create
variables ({
:dbcon => db,
:dbk => dbkey
})
end

Rails radio_button check conditionally in loop

I have radiobuttons creaated in loop.
- #annual_packages.each do |plan|
= f.radio_button('plan_id', plan.id)
= plan.title
How can I make condition to check radio if plan.id == #plan.id
Doesn't work:
= f.radio_button('plan_id', plan.id, checked: (plan.id == #plan.id))
Loop code:
= form_for #organization, url: subscription_create_path, remote: true, method: :post do |f|
- unless #annual_packages.blank?
- #annual_packages.each do |plan|
= f.radio_button('plan_id', plan.id)
= plan.title
Can you post your form_for code?
Cause if your column plan_id have any value, it should be checked automatically if it matches the tag_value`.
If the current value of method is tag_value the radio button will be
checked.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-radio_button
If you are using simple_form or form_for, and f is your form object then you can simply do:
for form_for:
radio_button(object_name, method, tag_value, options = {}) refer
in your case:
- #annual_packages.each do |plan|
radio_button("your object name", "plan_id", plan.id, options = {})
If the current value of method(object.plan_id) is tag_value(plan) the radio button will be checked.
for simple_form:
= f.input :plan_id, as: :radio_buttons, collection: #annual_packages

Filter in multiple parameters in query string

I've got a django app that has a filter as one of it's features.
The filter values are decided from a checkbox which is sent to the django backend using ajax as follows:
$('input.type-check').on('click', function(){
var a = $(this).prop('checked');
if(a == true){
elem.push($(this).attr('data-role'));
}else{
elem.splice($(this).attr('data-role'));
}
var cats = '';
$.each(elem, function(i){
cats += elem[i];
});
var xurl ='/filter?category='+cats;
$.ajax({
type: 'GET',
url: xurl,
success: function(data){
$('div.products').html(data);
}
})
});
The /filter$' url is mapped to thefitlered` view which is:
def filtered(request):
if 'category' in request.GET and request.GET['category']:
cat = request.GET['category']
ct = Product.objects.filter(category__in=cat)
diction = {'prods': ct}
return render(request, 'filter.html', diction)
It works when only one category is sent as parameter. However, when I send multiple, it gives no results.
Eg:
filter?category=Dairy will return the product that's associated with that category. However, filter?category=Dairy,Plastics or filter?category=DairyPlastics (which is from the above mentioned Javascript snippet) returns no result.
I've tried putting the category inside brackets in the view as follows [cat] but that doesn't help either. What should I do to make it return results?
The issue is, you are neither specifying a delimiter to demarcate the categories, not are you separating the categories in the view.
Try this:
In JQuery,
var cats = elem.join(', ')
var xurl ='/filter?category='+cats;
And in the view:
def filtered(request):
if request.GET.get('category'):
cat = request.GET.get'category')
cat_list = [c.strip() for c in cat.split(',')]
ct = Product.objects.filter(category__in=cat_list).distinct()
#You might need a distinct clause too, to remove duplicates
diction = {'prods': ct}
return render(request, 'filter.html', diction)
This would work for a single category v/s a list of categories

Accessing properties in templates by their name

given the following pieces of code:
groovy:
binding = [key1: "val1"]
def f = new File('test.template')
engine = new GStringTemplateEngine()
template = engine.createTemplate(f).make(binding)
println template.toString()
test.template:
<% keyName = "key1" %>
Is there a way to access val1 by keyName in test.template?
This:
${ binding[keyName] }
does not work (No such property: key1 for class: groovy.lang.Binding). Any ideas? Maybe the name of the map holding the properties is different?
I know I could just write:
${ key1 }
but I need to access property key1 using variable keyName.
Not sure if this is better but I got the following to work (somewhat)
Map binding = [ keyName: 'key1', key1: "val1", m: [key1:'val100', key2:'val2']]
def f = new File('test.template')
def engine = new groovy.text.GStringTemplateEngine()
def template = engine.createTemplate(f).make(binding)
println template.toString()
with the following template:
$keyName
$key1
<%= m[keyName] %>
But this relies on a submap that holds the values you are looking for.
I can see scenarios where in the binding, you pass a list of fields you want to process or display (rather than knowing them ahead of time), so you would have to get the field names from a well-known variable and then process the others possibly thru a submap.