I have a lot of helpers defined which all basically do the same.
def subtitle(page_subtitle)
content_for(:subtitle) { page_subtitle }
end
def header(page_header)
content_for(:header) { page_header }
end
def auto_header(page_auto_header)
content_for(:auto_header) { page_auto_header }
end
def header_image(page_header_image)
content_for(:header_image) { page_header_image }
end
def bodyclass(page_bodyclass)
content_for(:bodyclass) { page_bodyclass }
end
And there are many more...
My question is how can I DRY this code?
I tried something this but I didn't work
content_for_helpers = ["title","subtitle","logocolor"]
content_for_helpers.each do |helper|
def helper(helper)
content_for(helper.parameterize.underscore.to_sym) { helper }
end
end
def helper what
content_for(what) { send "page_#{what}" }
end
Related
Trying to use can? method and stub current ability.
class UserDecorator < Draper::Decorator
delegate_all
def some_helper
can? :read, object
end
end
RSpec.describe UserDecorator, type: :decorator do
before { allow(subject).to receive(:current_ability) { Ability.new } }
subject { User.new.decorate }
it { subject.some_helper }
end
But I get error: user does not implement: current_ability.
I'm working on a rails application with no models. I have a class in lib, FakeMaker, that builds up a bunch of fake entities for display purposed.
I want to test out deletion functionality but the problem is that my fake data set re-initializes every time I hit the controller.
I'd like to run the data test creator only once so that I only have one set of fake data.
I have tried using ||=, before_filter, class methods in FakeMaker, sessions storage but they all seem to have the issue of reinitializing the data set everytime the controller is hit.
Controller code:
class HomeController < ApplicationController
include FakeMaker
before_filter :set_up_fake_data
def index
#workstations = #data[:workstations]
#data_sources = #data[:data_sources]
end
private
def set_fake_data
#data ||= session[:fake_data]
end
def initialize_data
session[:fake_data] = set_up_fake_data
end
end
FakeMaker in lib:
module FakeMaker
include ActionView::Helpers::NumberHelper
SOURCE_CIDNE = "CIDNE"
SOURCE_DCGS = "DCGS"
TYPES_CIDNE = Faker::Lorem.words(num = 10)
TYPES_DCGS = Faker::Lorem.words(num = 4)
def set_up_fake_data
#data ||= { workstations: fake_maker("workstation", 8), data_sources: fake_maker("data_source", 2) }
end
def fake_maker(type, count)
fake = []
case type
when "report"
count.times { fake << fake_report }
when "workstation"
count.times { fake << fake_workstation }
when "data_source"
fake = fake_data_source
end
fake
end
def fake_report
report = { source: [SOURCE_CIDNE, SOURCE_DCGS].sample,
count: number_with_delimiter(Faker::Number.number(5), :delimiter => ',') }
report[:type] = report[:source] == SOURCE_CIDNE ? TYPES_CIDNE.sample : TYPES_DCGS.sample.capitalize
report
end
def fake_workstation
{ name: Faker::Lorem.word,
downloaded: number_with_delimiter(Faker::Number.number(3), :delimiter => ','),
available: number_with_delimiter(Faker::Number.number(5), :delimiter => ','),
last_connect: fake_time,
queueJSON: fake_queueJSON,
historyJSON: fake_historyJSON }
end
def fake_data_source
data_sources = []
["CIDNE", "DCGS"].each do |source|
data_sources << { type: source,
name: Faker::Internet.url,
status: ["UP", "DOWN"].sample }
end
data_sources
end
def fake_historyJSON
history = []
12.times { history << fake_history }
history
end
def fake_queueJSON
queue = []
35.times { queue << fake_queue }
queue
end
def fake_history
{ sentTimestamp: fake_time,
reportID: Faker::Number.number(5)}
end
def fake_queue
{ priority: Faker::Number.number(3),
queuedTimestamp: fake_time,
reportID: Faker::Number.number(5)}
end
def fake_time
Random.rand(10.weeks).ago.strftime("%Y-%m-%d %H:%M:%S")
end
end
I have to do a refactoring in an advanced search method with 500 lines. I split this method using closures in small parts, and now I have a lot of closures but I want to invoke them dynamically.
For example:
def listCriteria={ ... }
def textCriteria={ ... }
def booleanCriteria={ ... }
criteria.listDistinct {
criteries.eachWithIndex { crit, i->
def criteriaType="${crit.type}Criteria"
...
}
}
How can I do that?
Using methods you could dynamically call this methods from a string like this:
def listCriteria() {"list"}
def textCriteria() {"text"}
def string1 = "list"
def string2 = "text"
assert "${string1}Criteria"() == "list"
assert "${string2}Criteria"() == "text"
Edit:
I don't know an elegant way to get a dynamic reference to a closure.
You could use the properties property of your controller class to find all closures and invoke them.
def allClosures = this.properties.findAll{Closure.isAssignableFrom(it.value.getClass())}
def callCriteriaClosureByName(name) {
def criteriaClosure = allClosures.find{it.key == "${name}Criteria"}.value
if(criteriaClosure)
criteriaClosure()
}
Not that nice - but should work.
Closures are good for scoping. What about using a map?
class Criteria {
def listDistinct(closure) {
closure()
}
}
closures = [
listCriteria : { "list" },
textCriteria : { "text" },
booleanCriteria : { "boolean" }
]
def criteries = ["list", "text", "boolean"]
def criteria = new Criteria()
criteria.listDistinct {
criteries.eachWithIndex { crit, index ->
def criteriaType=closures["${crit}Criteria"]
assert criteriaType instanceof Closure
}
}
I have a few controller methods that are extremely similar and I was wondering what the best way to refactor them would be. First thing that comes to mind would be somehow passing in two blocks to a helper method, but I'm not sure how to do that either.
def action_a
if #last_updated.nil?
#variable_a = #stuff_a
else
#variable_a = (#stuff_a.select{ |item| item.updated_at > #last_updated }
end
end
def action_b
if #last_updated.nil?
#variable_b = #stuff_b.some_method
else
#variable_b = #stuff_b.some_method.select{ |stuff| item.updated_at > #last_updated }
end
end
It just seems like I'm constantly checking if #last_updated is nil (I set the #last_updated instance variable in a before_filter. If I could somehow pass the stuff inside the if as a block and the stuff in the else as another block, then I could remove the if #last_updated.nil? duplication?
What is the best way of accomplishing this for many methods?
Update
Where I specify #stuff_a and #stuff_b, they are always returning an array (since I use .select).
Take a look at this. It's DRYer and should yield identical results.
def action_a
do_the_processing :"#variable_a", #stuff_a
end
def action_b
do_the_processing :"#variable_b", #stuff_b.some_method
end
private
def do_the_processing var_name, collection
if #last_updated.nil?
instance_variable_set var_name, collection
else
instance_variable_set var_name, collection.select{ |item| item.updated_at > #last_updated }
end
end
Update
And here's the two blocks approach (just for fun) (uses 1.9's stabby lambda syntax)
def action_a
check_last_updated is_nil: -> { #variable_a = #stuff_a },
is_not_nil: -> { #variable_a = (#stuff_a.select{ |item| item.updated_at > #last_updated } }
end
def action_b
check_last_updated is_nil: -> { #variable_b = #stuff_b.some_method },
is_not_nil: -> { #variable_b = #stuff_b.some_method.select{ |stuff| item.updated_at > #last_updated } }
end
private
def check_last_updated blocks = {}
if #last_updated.nil?
blocks[:is_nil].try(:call)
else
blocks[:is_not_nil].try(:call)
end
end
You need to extract your condition in a separate def block and use it later on:
def select_updates a
#last_updated.nil? ? a : a.select{ |item| item.updated_at > #last_updated }
end
def action_a; #variable_a = select_updates(#stuff_a) end
def action_b; #variable_b = select_updates(#stuff_b.some_method) end
AS I can see, you could do the followings
have two scope for each
Ex:
class Stuff < ActiveRecord::Base
scope :updated_at, lambda {|updated_date|
{:conditions => "updated_at > #{updated_date}"}
}
end
class Item < ActiveRecord::Base
scope :updated_at, lambda {|updated_date|
{:conditions => "updated_at > #{updated_date}"}
}
end
in your controller do this
def action_a
#variable_a = update_method(#stuff_a)
end
def action_b
#variable_b = update_method(#stuff_b)
end
private
def update_method(obj)
result = nil
if #last_updated.nil?
result = obj.some_method
else
result = obj.some_method.updated_at(#last_updated)
end
result
end
HTH
How can I rewrite this code so it's completely dynamic, and I don't have to use the case clause to manually list all possible values of #group?
# Grouping
#group = params[:group] if !params[:group].blank?
case #group
when 'category_id'
#ideas_grouped = #ideas.group_by { |i| i.category_id }
when 'status_id'
#ideas_grouped = #ideas.group_by { |i| i.status_id }
when 'personal_bias'
#ideas_grouped = #ideas.group_by { |i| i.personal_bias }
when 'business_value'
#ideas_grouped = #ideas.group_by { |i| i.business_value }
end
You can use some sort of meta programming
Above code can be refactored in one of the way is
if params[:group].present? && ["category_id","status_id","personal_bias","business_value"].include?(params[:group])
#ideas_grouped = #ideas.group_by { |i| i.send(params[:group]) }
end
If you need no white-listing:
#ideas_grouped = if (group = params[:group]).present?
#ideas.group_by(&group.to_sym)
end
If you need white-listing you may call include? first (see Amar's answer), but to add something new, let me push it with a declarative approach (Object#whitelist is left as an exercise for the reader, maybe comes from Ick):
#ideas_grouped = params[:group].whitelist(IdeaGroupers).maybe do |group|
#ideas.group_by(&group.to_sym)
end
Try out this:
#ideas_grouped = #ideas.group_by { |i| i.send(:"#{#group}")} if (#group = params[:group])
what about :
#group = params[:group] if !params[:group].blank?
#ideas_grouped = ideas_hash.fetch(#group)
def ideas_hash
{
'category_id' => ideas_by_category_id,
'status_id' => ideas_by_status_id,
'personal_bias' => ideas_by_personal_bias
'business_value' => ideas_by_business_value
}
end
def ideas_by_category_id
#ideas.group_by { |i| i.category_id }
end
def ideas_by_status_id
#ideas.group_by { |i| i.status_id }
end
def ideas_by_personal_bias
#ideas.group_by { |i| i.personal_bias }
end
def ideas_by_business_value
#ideas.group_by { |i| i.business_value }
end
I would also make ideas_hash and all of the others method private.
Okay, the last time I touched Ruby is too far back so I cannot give you an example. As far as I understand your problem you are doing a mapping (group -> accessor method) there. So either you use a map object or you build a mapping function using a lambda.