Write simple rails code better - ruby-on-rails

I'm newbie on rails.
In my form I get string like "123, xxx_new item, 132, xxx_test "
if the item start with "xxx_" than its mean that i should add the item to the db otherwise enter the value
this is my code and i sure that there is a better way to write this code
tags = params[:station][:tag_ids].split(",")
params[:station][:tag_ids] = []
tags.each do |tag|
if tag[0,4] =="xxx_"
params[:station][:tag_ids] << Tag.create(:name => tag.gsub('xxx_', '')).id
else
params[:station][:tag_ids]<< tag
end
end
I'm looking for how to improve my code syntax

What about:
tags = params[:station][:tag_ids].split(',')
params[:station][:tag_ids] = tags.each_with_object([]) do |tag, array|
array << tag.start_with?('xxx_') ? Tag.create(name: tag[4..-1]).id : tag
end

Related

Simpler way to alternate upper and lower case words in a string

I recently solved this problem, but felt there is a simpler way to do it. I looked into inject, step, and map, but couldn't figure out how to implement them into this code. I want to use fewer lines of code than I am now. I'm new to ruby so if the answer is simple I'd love to add it to my toolbag. Thank you in advance.
goal: accept a sentence string as an arg, and return the sentence with words alternating between uppercase and lowercase
def alternating_case(str)
newstr = []
words = str.split
words.each.with_index do |word, i|
if i.even?
newstr << word.upcase
else
newstr << word.downcase
end
end
newstr.join(" ")
end
You could reduce the number of lines in the each_with_index block by using a ternary conditional (true/false ? value_if_true : value_if_false):
words.each.with_index do |word, i|
newstr << i.even? ? word.upcase : word.downcase
end
As for a different way altogether, you could iterate over the initial string, letter-by-letter, and then change the method when you hit a space:
def alternating_case(str)
#downcase = true
new_str = str.map { |letter| set_case(letter)}
end
def set_case(letter)
#downcase != #downcase if letter == ' '
return #downcase ? letter.downcase : letter.upcase
end
We can achieve this by using ruby's Array#cycle.
Array#cycle returns an Enumerator object which calls block for each element of enum repeatedly n times or forever if none or nil is given.
cycle_enum = [:upcase, :downcase].cycle
#=> #<Enumerator: [:upcase, :downcase]:cycle>
5.times.map { cycle_enum.next }
#=> [:upcase, :downcase, :upcase, :downcase, :upcase]
Now, using the above we can write it as following:
word = "dummyword"
cycle_enum = [:upcase, :downcase].cycle
word.chars.map { |c| c.public_send(cycle_enum.next) }.join("")
#=> "DuMmYwOrD"
Note: If you are new to ruby, you may not be familiar with public_send or Enumberable module. You can use the following references.
Enumberable#cycle
#send & #public_send

ruby add strings from array

I am building a simple breadcrumb in ruby but I am not sure how to really implement my logic.
Let's say I have an array of words that are taken from my request.path.split("/) ["", "products", "women", "dresses"]
I want to push the strings into another array so then in the end I have ["/", "/products", "products/women", "products/women/dresses"] and I will use it as my breadcrumb solution.
I am not good at ruby but for now I came up with following
cur_path = request.path.split('/')
cur_path.each do |link|
arr = []
final_link = '/'+ link
if cur_path.find_index(link) > 1
# add all the previous array items with the exception of the index 0
else
arr.push(final_link)
end
end
The results should be ["/", "/products", "/products/women", "/products/women/dresses"]
Ruby's Pathname has some string-based path manipulation utilities, e.g. ascend:
require 'pathname'
Pathname.new('/products/women/dresses').ascend.map(&:to_s).reverse
#=> ["/", "/products", "/products/women", "/products/women/dresses"]
This is my simplest solution:
a = '/products/women/dresses'.split('/')
a.each_with_index.map { |e,i| e.empty? ? '/' : a[0..i].join('/') }
Using map and with_index it can be done like this:
arr = ["", "products", "women", "dresses"]
arr.map.with_index { |item, index| "#{arr[0...index].join('/')}/#{item}" }
This is another option using Enumerable#each_with_object and Enumerable#each_with_index:
ary = '/products/women/dresses'.split('/')
ary[1..].map
.with_index
.with_object([]) { |(folder, idx), path| path << [path[idx-1], folder].join('/') }.unshift('/')
Or also:
(ary.size - 1).times.map { |i| ary.first(i + 2).join('/') }.unshift('/')

Call the same function on a list and return a list with no duplicates?

I have this function:
medIntCategory = MedicalInterventionCategory.find_by_category_text(category.category.text)
However now I have a list of categories called categories.
I would like to execute the above code for each category and get back a list of medIntCategories, but with no duplicates.
Is there a simple way to do this since I am only dealing with integers?
in simple terms:
categoryList = []
for each category in categories do
categoryList += MedicalInterventionCategory.find_by_category_text(category.category.text)
end
But with duplicate checking
This sounds like a job for Array#map and Array#uniq:
category_list = categories.map{|category|
MedicalInterventionCategory.find_by_category_text(category.category.text)
}.uniq
#result=Array.new
##assuming that it returns an array
medIntCategory = MedicalInterventionCategory.find_by_category_text(category.category.text)
##get the first category obtained
#result << medIntCategory
if medIntCategory.present?
medIntCategory.each do |m|
##add in same array only if not present
if !#result.include?(m)
#result << m.find_by_category_text(c.category.text)
end
end
##return a unique value array
#result.flatten.compact.uniq unless #result.blank?
end
HOPE IT HELPS
I think this would work
category_list = []
categories.each do |category|
category_list << MedicalInterventionCategory.find_by_category_text(category.category.text).distinct
end

Rspec, Rails - how to test helper method that use params hash?

I want to implement a tagging system similar to stackoverflow, there is a box with a tags at top right corner, and also I have links to delete tag from params hash. my method works correctly in browser. But I can't find a way to test it.
def tags_list_with_destroy_links
if params[:tags]
li = ""
p = params[:tags].split("+") # '/tagged/sea+ship+sun' => ['sea', 'ship', 'sun']
p.map do |t|
remove_link = if p.count >= 3
c = p.reject {|item| item == t }
a = c.join("+")
{:tags => a}
elsif p.count == 2
c = p.reject {|item| item == t }
{tags: c[0]}
else
questions_url
end
li << content_tag(:li) do
link_to(t, questions_tags_path(t), class: 'tag') +
link_to( '', remove_link , class: 'icon-small icons-cross')
end
end
ul = content_tag(:ul, li.html_safe)
ul << tag(:hr)
end
end
I've tried:
it 'return list with selected tags' do
#Rails.application.routes.url_helpers.stub(:questions_tags).and_return('/questions/tagged/sea+ship+sun')
#helper.request.stub(:path).and_return('/questions/tagged/sea+ship+sun')
helper.stub(:url_for, {controller:'questions', action: 'index', tags:'sea+ship+sun'} ).and_return('/questions/tagged/sea+ship+sun')
helper.params[:tags] = 'sea+ship+sun'
helper.tags_list_with_destroy_links.should == 'list_with_tags'
end
but it return:
<a class=\"tag\" href=\"/questions/tagged/sea+ship+sun\">sea</a><a class=\"icon-small icons-cross\" href=\"/questions/tagged/sea+ship+sun\"></a></li>
and shoud return remove link as
href="/questions/tagged/ship+sun" without sea
I would appreciate any advice
The params field is going to come back parsed into the correct ruby data structures (hash, array, string, etc). There's no need to manually split items such as +, if there is a nested param it will return as part of the params object:
{tags: ["sea", "ship", "sun"]}
To access your data, or create an assumption about your param data existing in the test, you're going to want to create a stub. You're almost there, try something more along the lines of:
helper.stub!(:params).and_return({tags: ["sea", "ship", "sun"]})
Once you have the params stubbed correctly you can check the output of your helper method to ensure it's validity (this is called the expectation):
expect(helper.tags_list_with_destroy_links).to eq("some_url")

One ' to much in string

I try to write to an string something like this:
arr << "Icd3code.create!({:text => '#{variable1}'})" + "\n"
My problem is that variable 1 is an string, that contains an ' :
variable1 = "Ami's house"
So that at the end the ouput of my code is this:
Icd3code.create!({:text => 'Ami's house'})
How you can see now i have one ' to much! I dont know what i can do to avoid this problem! Thanks
If I've understood, you want to loop over some input, building up a list of parameters, which you plan to later use to create some records. If that's the case, I think you're better off using hashes, instead of strings:
# Let's pretend this came from the big, bad, world
inputs = ["Ami's house", "Fred's house", "Jim's house"]
creation_params = []
inputs.each do |input|
creation_params << {:text => input}
end
Then you could create all the Icd3codes, like this:
creation_params.each do |params|
Icd3code.create!(params)
end
Or you could save them in a text file, for later:
File.open('dest', 'w') do |f|
f.write(creation_params.to_json)
end
variable1 = "Ami's house"
puts %Q[Icd3code.create!({:text => "#{variable1}"})] + "\n"
--output:--
Icd3code.create!({:text => "Ami's house"})

Resources