Capybara::Ambiguous match, found 2 elements matching xpath - Find 1st link - ruby

I'm having an index action where I list all blog posts
<% #posts.each do |post| %>
<div class="blog-post">
<h2 class="blog-post-title"><%= link_to post.title, post_path(post) %></h2>
<p><%= post.sort_description %></p>
<p class="blog-post-meta">
<%= link_to 'Read more', post_path(post) %>
</p>
</div>
<% end %>
In my test script, in order to access the show action and view a single post I have this
find(:xpath, "//a[#href='/posts/1']").click
# or click_link(href: post_path(post))
But when I try to run the test I get this error
Failure/Error: find(:xpath, "//a[#href='/posts/1']").click
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching xpath "//a[#href='/posts/1']"
as capybara finds two different links which go to same page (one on title and the "read more" link). Is there a way to tell capybara to use the first link that finds?

Since one of the links is in the title h2 you can use that to scope the find and remove the ambiguity
find(".blog-post-title > a[href=`#{post_path(post)}`]").click # always better to use post_path than hardcode the id
You could also do first(:link, href: post_path(post)).click but first (like all) has the disadvantage of not having waiting/retrying behavior so unless you're sure the page is fully loaded when called it's best to avoid it (or enable waiting/retrying on it by specifying one of the count options first(:link, href: post_path(post), minimum: 1).click).
If you need to click blog title links a lot you could also create a custom selector with something like
Capybara.add_selector(:title_link) do
css do |post|
".blog-post-title > a[href=`#{post_path(post)}`]"
end
end
which would then allow you to do
find(:title_link, post).click

You don't have to use xpath.
In your example, you should be able to use:
first('.blog-post-title > a').click

Related

Linking to search results with Ruby

I'm a complete novice in Ruby and Nanoc, but a project has been put in my lap. Basically, the code for the page brings back individual URLs for each item linking them to the manual. I'm trying to create a URL that will list all of the manuals in one search. Any help is appreciated.
Here's the code:
<div>
<%
manuals = #items.find_all('/manuals/autos/*')
.select {|item| item[:tag] == 'suv' }
.sort_by {|item| item[:search] }
manuals.each_slice((manuals.size / 4.0).ceil).each do |manuals_column|
%>
<div>
<% manual_column.each do |manual| %>
<div>
<a href="<%= app_url "/SearchManual/\"#{manual[:search]}\"" %>">
<%= manual[:search] %>
</a>
</div>
<% end %>
</div>
<% end %>
</div>
As you didn't specify what items is returning, I did an general example:
require 'uri'
# let suppose that your items query has the follow output
manuals = ["Chevy", "GMC", "BMW"]
# build the url base
url = "www.mycars.com/search/?list_of_cars="
# build the parameter that will be passed by the url
manuals.each do |car|
url += car + ","
end
# remove the last added comma
url.slice!(-1)
your_new_url = URI::encode(url)
# www.mycars.com/?list_of_cars=Chevy,GMC,BMW
# In your controller, you will be able to get the parameter with
# URI::decode(params[:list_of_cars]) and it will be a string:
# "Chevy,GMC,BMW".split(',') method to get each value.
Some considerations:
I don't know if you are gonna use this on view or controller, if will be in view, than wrap the code with the <% %> syntax.
About the URL format, you can find more choices of how to build it in:
Passing array through URLs
When writing question on SO, please, put more work on that. You will help us find a quick answer to your question, and you, for wait less for an answer.
If you need something more specific, just ask and I can see if I can answer.

Rails route, show all elements on the same page

I need to show all my elements on the same page.
In routes:
namespace :nourishment do
resources :diets do
resources :nourishment_meals, :controller => 'meals'
get 'nourishment_meals/show_all_meals' => 'meals#show_all_meals', as: "show_all_meals"
end
end
which will generate:
nourishment_diet_nourishment_meals_path GET /nourishment/diets/:diet_id/nourishment_meals(.:format) nourishment/meals#index
POST /nourishment/diets/:diet_id/nourishment_meals(.:format) nourishment/meals#create
new_nourishment_diet_nourishment_meal_path GET /nourishment/diets/:diet_id/nourishment_meals/new(.:format) nourishment/meals#new
edit_nourishment_diet_nourishment_meal_path GET /nourishment/diets/:diet_id/nourishment_meals/:id/edit(.:format) nourishment/meals#edit
nourishment_diet_nourishment_meal_path GET /nourishment/diets/:diet_id/nourishment_meals/:id(.:format) nourishment/meals#show
PATCH /nourishment/diets/:diet_id/nourishment_meals/:id(.:format) nourishment/meals#update
PUT /nourishment/diets/:diet_id/nourishment_meals/:id(.:format) nourishment/meals#update
DELETE /nourishment/diets/:diet_id/nourishment_meals/:id(.:format) nourishment/meals#destroy
[**THIS**]
nourishment_diet_show_all_meals_path GET /nourishment/diets/:diet_id/nourishment_meals/show_all_meals(.:format) nourishment/meals#show_all_meals
The problem, when I do this:
<%= link_to "Show all meals", nourishment_diet_show_all_meals_path, :class=>"button green" %>
This error raise:
Problem:
Problem:
Document(s) not found for class NourishmentMeal with id(s) show_all_meals.
Summary:
When calling NourishmentMeal.find with an id or array of ids, each parameter must match a document in the database or this error will be raised. The search was for the id(s): show_all_meals ... (1 total) and the following ids were not found: show_all_meals.
Resolution:
Search for an id that is in the database or set the Mongoid.raise_not_found_error configuration option to false, which will cause a nil to be returned instead of raising this error when searching for a single id, or only the matched documents when searching for multiples.
The error is here, on my meals_controller.rb
private
# Use callbacks to share common setup or constraints between actions.
def set_nourishment_meal
#nourishment_diet = NourishmentDiet.find(params[:diet_id])
[***HERE***] #nourishment_meal = #nourishment_diet.meals.find(params[:id])
end
Method:
def show_all_meals
puts "This word does not appear"
end
Can someone help me?
The route below expects a :diet_id. A diet instance has to be provided as an argument for this path to call corresponding action.
nourishment_diet_show_all_meals_path GET /nourishment/diets/:diet_id/nourishment_meals/show_all_meals(.:format) nourishment/meals#show_all_meals
This should be changed:
<%= link_to "Show all meals", nourishment_diet_show_all_meals_path, :class=>"button green" %>
to:
<%= link_to "Show all meals", nourishment_diet_show_all_meals_path(diet), :class=>"button green" %>
Notice the argument (diet) above.
I think you should pass diet_id parameter in params. You should try something like this: <%= link_to "Show all meals", nourishment_diet_show_all_meals_path(#diet.id), :class=>"button green" %>. #diet.id is just an example, use whatever works for your application.

How to generate pages for each tag in nanoc

I am new to nanoc and I am still finding my around it. I am able to get my site ready, it looks good and functions good, too. But I need to have a tags area. I am able to achieve that with
<%= tags_for(post, params = {:base_url => "http://example.com/tag/"}) %>
But how do I generate pages for tag? So for instance there is a tag called "NFL", so every time a user clicks on it, he/she should be directed to http://example.com/tag/nfl with a list of articles that correspond with NFL.
I can setup a layout which will do that. But then what kind of logic should be I using? And also do I need to have a helper for this?
You can use a preprocess block in your Rules file in order to generate new items dynamically. Here’s an example of a preprocess block where a single new item is added:
preprocess do
items << Nanoc::Item.new(
"some content here",
{ :attributes => 'here', :awesomeness => 5000 },
"/identifier/of/this/item")
end
If you want pages for each tag, you need to collect all tags first. I’m doing this with a set because I do not want duplicates:
require 'set'
tags = Set.new
items.each do |item|
item[:tags].each { |t| tags.add(t.downcase) }
end
Lastly, loop over all tags and generate items for them:
tags.each do |tag|
items << Nanoc::Item.new(
"",
{ :tag => tag },
"/tags/#{tag}/")
end
Now, you can create a specific compilation rule for /tags/*/, so that it is rendered using a "tags" layout, which will take the value of the :tag attribute, find all items with this tag and show them in a list. That layout will look somewhat like this:
<h1><%= #item[:tag] %></h1>
<ul>
<% items_with_tag(#item[:tag]).each do |i| %>
<li><%= link_to i[:title], i %></li>
<% end %>
</ul>
And that, in broad strokes, should be what you want!

Middleman variables visibility in config.rb and pages

I'm missing something about visibility of variables.
In my config.rb i'm using a data structure to generate dynamic pages:
#pages = [
{
id: "cookies",
title: "Happy Chocolate Chip Cookies",
quote: "These cute cookies are full of sweet chocolate and ready to give you energy!",
content: "Orecchini a monachella. Realizzati in fimo, dipinti a mano e rivestiti con vernice lucida."
},
....]
#pages.each do |p|
page "/creations/#{p[:id]}.html", :proxy => "item-template.html", :ignore => true do
#tile = p
end
end
The pages generation goes well, no problem with that. But..
How can i access this data structure also in order to provide dynamic links to the generated pages? I would like to be able to create an index page (let's call it creations.html) with the following code:
<ul>
<% #pages.each do |tile| %>
<li><a href="creations/<%= tile[:id]%>.html">
<%= tile[:title] %>
</a></li>
<% end %>
</ul>
No need to create a custom helper, you can use a yaml data file to populate your template and generate the list of links. Let me explain.
On the same level as source and build directories make sure you create a data directory.
ie:
build
source
data
Inside this create a file called "pages.yml" (for example).
This file needs to be specifically formatted so be careful (or use a yaml parser to make sure you don't have any errors - like a missing comma or escaped quote).
Using the data in your config.rb file, an example would be something like:
- id: "cookies"
title: "Happy Chocolate Chip Cookies"
quote: "These cute cookies are full of sweet chocolate and ready to give you energy!"
content: "Orecchini a monachella. Realizzati in fimo, dipinti a mano e rivestiti con vernice lucida."
- id: "bacon"
title: "Smoked bacon bits"
quote: "everything tastes better with bacon!"
content: "blah"
etc...
Now, in your config.rb file replace #pages.each do |p| with data.pages.each do |p|
data.pages.each loops through each item in the newly created yaml file
You can then simply reference the same file in your index file (creations.html) like so:
<ul>
<% data.pages.each do |tile| %>
<li><a href="creations/<%= tile[:id]%>.html">
<%= tile[:title] %>
</a></li>
<% end %>
</ul>
I had a similar problem around dynamic pages which you can refer to here
Hope this helps. Good luck!
Perhaps add a helper that returns the #pages data structure in your file creations.erb. I.e. in your config.rb file add:
helpers do
def dynamic_pages()
#pages
end
end
and then in your creations.erb have:
<ul>
<% dynamic_pages.each do |tile| %>
<li><a href="creations/<%= tile[:id]%>.html">
<%= tile[:title] %>
</a></li>
<% end %>
</ul>
And, if you want to reference dynamic pages in your dynamic pages(!), a helper could generate that html and you could call the ... nah, never mind!

Rails 3.1 - Correct approach to remove a query from a view and process it in the controller or model

In a view I have the following:
<% #top_posts.each do |post| %>
<li>
<%= post.title %><br />
<%= link_to "Most popular comment", comment_path( post.comments.order("vote_cnt DESC").first )
</li>
<% end %>
I know it is considered poor form to have the post.comments.order("vote_cnt DESC").first query in a view. However, since I'm combining both post and comment data to create a single list item, I'm having a hard time understanding how to get this "combo-pack" of data built in the controller. Should I be constructing some sort of #hash in the controller and then iterate on #hash.each in the view? Is that the right approach?
Or is the job for a scope on my Post model? Is there some ActiveRecord magic that I'm missing that makes this easy? I'm still pretty rookie at RoR, and am just beginning to see just how much I don't understand.
What I'd do is add a method in my Post model to get the first comment according to your criterias. The view would then look like comment_path(post.relevant_comment).

Resources