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

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")

Related

Rails Admin routes inside config

I'm using Rails Admin for my admin area.
The sidebar panel should have some links to the instances of a model.
In rails_admin.rb I've tried something like:
RailsAdmin.config do |config|
#navigation_links = Hash[*Conference.all.map {|conference| [conference.name, bindings[:view].main_app.show_path(model_name: 'conference', id: conference.id)]}.flatten]
config.navigation_static_links = #navigation_links
end
However, here I do not have access to bindings. So, how can I get the url of an admin resource here? I cannot see it in the documentation
Thanks
My answer probably not what you want to do, but it can be helpful.
I've checked rails_admin.gem and i found that there are two methods that responsible for rendering sidebar menu.
def main_navigation
nodes_stack = RailsAdmin::Config.visible_models(controller: controller)
node_model_names = nodes_stack.collect { |c| c.abstract_model.model_name }
nodes_stack.group_by(&:navigation_label).collect do |navigation_label, nodes|
nodes = nodes.select { |n| n.parent.nil? || !n.parent.to_s.in?(node_model_names) }
li_stack = navigation nodes_stack, nodes
label = navigation_label || t('admin.misc.navigation')
%(<li class='dropdown-header'>#{capitalize_first_letter label}</li>#{li_stack}) if li_stack.present?
end.join.html_safe
end
Method above responsible for getting list of models, especially:
nodes_stack = RailsAdmin::Config.visible_models(controller: controller)
Second method responsible for rendering item in the menu (aka li):
def navigation(nodes_stack, nodes, level = 0)
nodes.collect do |node|
model_param = node.abstract_model.to_param
url = url_for(action: :index, controller: 'rails_admin/main', model_name: model_param)
level_class = " nav-level-#{level}" if level > 0
nav_icon = node.navigation_icon ? %(<i class="#{node.navigation_icon}"></i>).html_safe : ''
li = content_tag :li, data: {model: model_param} do
link_to nav_icon + capitalize_first_letter(node.label_plural), url, class: "pjax#{level_class}"
end
li + navigation(nodes_stack, nodes_stack.select { |n| n.parent.to_s == node.abstract_model.model_name }, level + 1)
end.join.html_safe
end
So you can patch this methods to get what you need.
module RailsAdmin
module ApplicationHelper
def main_navigation
# your code
end
end
end
rails_admin.gem module
P.S. I love what you can read from rails doctrine about monkey patching:
This power has frequently been derided as simply too much for mere
mortal programmers to handle.

Database driven radio button ruby on rails

I am trying to update my conditions table with the radio button's value on and off
Here is the view
= form_tag('/admin/save',:action => 'update') do
= radio_button_tag("#{cols}_#{id}",1,checked = eval(check), options = {})
= radio_button_tag("#{cols}_#{id}",0,checked = eval(negcheck), options = {})
= submit_tag
Here is the controller
def updateCondition
params.each do |keys , value|
key ="#{keys}"
condition = key.split("_")[0]
hospitalid =key.split("_")[1]
if condition == "utf8" || condition == "authenticity" || condition == "commit"
next
end
Condition.find(hospitalid).update(:"#{condition}" => params["#{condition}_#{hospitalid}"])
end
render nothing: true
end
Params are :
{"utf8"=>"✓",
"authenticity_token"=>"",
"abc_10000"=>"1",
"commit"=>"Save changes",
"def_10000"=>"1",
}
Here is my question :
1) Why params is not showing all my radio button values ?
2) Any way to handle utf8, authentication_token, commit other than if statement ?
This may be why params not showing:
This looks strange to me: checked = eval(check) should it be checked: eval(check) or checked == eval(check)
Why are you setting options = {}? I think options should be passed in as a hash or not passed in at all.
Regarding #2,
if condition == "utf8" || condition == "authenticity" || condition == "commit"
next
end
Condition.find(hospitalid).update(:"#{condition}" => params["#{condition}_#{hospitalid}"])
You could extract that into a separate function:
def condition_valid?
condition == "utf8" || condition == "authenticity" || condition == "commit"
end
You're asking two questions here which is bad protocol for SO. I'll answer the second one.
Rather than try to filter out the parameters you don't want, which is very fragile, you could send through all the things you are trying to process, grouped into a single param. Eg, in your form, change the radio buttons to
= form_tag('/admin/save',:action => 'update') do
= radio_button_tag("conditions[#{id}][#{cols}]"}",1,checked = eval(check), options = {})
= radio_button_tag("conditions[#{id}][#{cols}]"}",0,checked = eval(negcheck), options = {})
= submit_tag
this will give you params like
{"utf8"=>"✓",
"authenticity_token"=>"",
:conditions => {1000 => {:abc => 1, :def => 1}},
"commit"=>"Save changes"
}
See how they are organised and separated? This means you don't have to muck about splitting them up again.
Now your controller code could be:
def updateCondition
params[:conditions].each do |id , attributes|
if condition = Condition.find_by_id(id)
condition.update_attributes(attributes)
end
end
render nothing: true
end
Note: you don't say what the value of "cols" is when you are building the form so i'm having to guess a bit. I think i just copied the way you are using it but shuffled it around a bit. There is almost certainly a cleaner way to build the form too.

Rails: each for multiple and one object data

I'm new in rails and need to clear one question:
for example my method return such data:
#<Article ART_ID: 1151754, ART_ARTICLE_NR: "0 281 002 757", ART_SUP_ID: 30, ART_DES_ID: nil, ART_COMPLETE_DES_ID: 62395, ART_CTM: nil, ART_PACK_SELFSERVICE: 0, ART_MATERIAL_MARK: 0, ART_REPLACEMENT: 0, ART_ACCESSORY: 0, ART_BATCH_SIZE1: nil, ART_BATCH_SIZE2: nil, datetime_of_update: "2012-09-25 17:49:18">
or array, not only one object: how could use each func then?
for example:
articles = ArtLookup.search_strong_any_kind_without_brand(params[:article_nr].gsub(/[^0-9A-Za-z]/, ''))
binding.pry
if articles.present?
articles.each do |a|
#all_parts_result <<
{
analogue_manufacturer_name: a.supplier.SUP_BRAND,
analogue_code: a.ART_ARTICLE_NR,
delivery_time_min: '',
delivery_time_max: '',
min_quantity: '',
product_name: a.art_name,
quantity: '',
price: '',
distributor_id: '',
link_to_tecdoc: a.ART_ID
}
end
end
now i get errors like
`undefined method `each' for `#<Article:0x007f6554701640>
i think it is becouse i have sometimes one object, sometimes 10, and sometime 0.
how is it beatifull and right to do in rails?
Your search_strong_any_kind_without_brand method is looping through your articles based on the search condition. If the article matches then you are setting #art_concret to the match and then returning the match. However, you're not finding all matches, just the last one.
.
loop
#art_concret = art
end
.
return #art_concret
If you set the #art_concret as an array and inject results into this instance variable, then you will have the resulting search in array form. However, keep in mind that this does kind of break the ActiveRecord ORM as you would be returning a simple array and not an ActiveRecord Relation array.
def self.search_strong_any_kind_without_brand(search)
search_condition = search.upcase
#art_concret = []
#search = find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition])
#articles = Article.find(:all, :conditions => ["ART_ID in (?)", #search.map(&:ARL_ART_ID)])
#binding.pry
#articles.each do |art|
if art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search
#art_concret << art
end
end
return #art_concret
end
If you want to keep the code a bit cleaner then use select on your matching condition instead of looping through each article in #articles.
def self.search_strong_any_kind_without_brand(search)
search_condition = search.upcase
#search = find(:all, :conditions => ['MATCH (ARL_SEARCH_NUMBER) AGAINST(? IN BOOLEAN MODE)', search_condition])
#articles = Article.find(:all, :conditions => ["ART_ID in (?)", #search.map(&:ARL_ART_ID)])
#binding.pry
return #articles.select { |art| art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search }
end
Unrelated: is there a reason why you're using instance variables in search_strong_any_kind_without_brand?
I think the right thing to do is to make sure your method always returns an array (or enumerable).
looking at the code you posted in to pastebin I would recommend you use Array#select in your method
for example you might be able to just return this:
#articles.select { |art| art.ART_ARTICLE_NR.gsub(/[^0-9A-Za-z]/, '') == search }
assuming #articles is an array or collection you will always get an array back, even if it is 0, or 1 element
This answer would be a bit offtopic, but I would like to mention a splat operator:
[*val]
will produce array, consisting of either single val value whether it’s not an array, or the array itself whether val is an array:
▶ def array_or_single param
▷ [*param].reduce &:+ # HERE WE GO
▷ end
=> :array_or_single
▶ array_or_single [1,2,3]
=> 6
▶ array_or_single 5
=> 5
That said, you code would work with this tiny improvement:
- articles.each do |a|
+ [*articles].each do |a|
Hope it gives a hint on how one might handle the data, coming from the 3rd party. As an answer to your particular question, please follow the advises in the other answers here.

Write simple rails code better

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

Params contain an array that wants to be a hash

I have an array (coming from a file_field, :multiple => true) in my params that I want to turn into a hash so I can build associated models for each element and process in my create action.
Currently receiving:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>[#<1st Image data removed for brevity>, #<2nd Image data removed for brevity>]}}}, "commit"=>"Save"}
I'd like to turn it into something like:
{"gallery"=>{"name"=>"A Gallery", "photos_attributes"=>{"0"=>{"image"=>#<1st Image data removed for brevity>}, "1"=>{"image"=>#<1st Image data removed for brevity>}}}, "commit"=>"Save"}
considered something like this but it's clearly wrong:
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[i++.to_s] = element
end
What's the "Rail's Way"?
You need to return the result hash at the end of each iteration.
i = 0
params[:gallery][:photos_attributes]["0"][:image].reduce({}) do |result, element|
result[(i += 1).to_s] = element
result
end
I've done something similar when receiving data from an iOS device. But, if I understand what you want and what your model(s) look like, to get nested attributes to work you don't want it to look like:
{ "photos_attributes" => { "0" => <image1>, "1" => <image2>, ... }
You want it to look like:
{ "photos_attributes" => [ <image1>, <image2>, ... ] }
And to do that all you need to do is:
params["gallery"]["photos_attributes"] = params["gallery"]["photos_attributes"]["0"]["image"]
Now, if I've misunderstood what you need, to get what you've asked for what you have might work (I don't use much reduce aka inject) or you could use tap:
i = 0
params["gallery"]["photos_attributes"] = {}.tap do |hash|
params["gallery"]["photos_attributes"]["0"]["image"].each do |image|
hash[i.to_s] = image
i = i + 1
end
end
Not a whole lot better IMO.

Resources