Linking to search results with Ruby - 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.

Related

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

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

How to reuse ERB templating code in function?

I have this piece of code inside ERB template to render all articles in list.
<% #list.each do |a| %>
<article class="my-panel">
<header>
<h2><%= a[:name] %></h2>
<time datetime="<%= to_datetime(a[:time]) %>"><%= time_description(a[:time]) %></time>
</header>
... more stuff cut out
</article>
<% end %>
Now I am going to have to change it to something like this:
<% #list[content_splitter.before_ad_range].each do |a| %>
<%= render_article(a) %>
<% end %>
<%= AdCreator.between_content_ad %>
<% #list[content_splitter.after_ad_range].each do |a| %>
<%= render_article(a) %>
<% end %>
I thought it would be nice to have render_article defined in template as opposed to having html clutter my ruby code. But when I move that code inside function I get an error.
This is the function:
<% def render_article(a) %>
<article class="my-panel">
<header>
<h2><%= a[:name] %></h2>
<time datetime="<%= to_datetime(a[:time]) %>"><%= time_description(a[:time]) %></time>
</header>
<div class="image">
<img alt="" src="<%= rel_url_to a[:img_url_1x] %>" srcset="<%= rel_url_to a[:img_url_2x] %> 2x, <%= rel_url_to a[:img_url_3x] %> 3x">
</div>
<div class="text">
<%= a[:article_text] %>
</div>
</article>
<% end %>
This is the error:
undefined local variable or method `_erbout' for #<Html::FrontPage:0x0055fb94005c68>
Line of code producing this error is:
self.class.instance_variable_get(:#renderer).result(binding)
Why is this happening? How to find more informative error?
How to fix this? Can I avoid moving this obviously html dominant code into ruby helper file?
PS. I suspect problem is that functions to_datetime and time_description can't be accessed from inside ERB function.
I know that function render_article does get called because if I change it's signature to remove parameter I get an error
wrong number of arguments (given 1, expected 0)
# (erb):45:in `render_article'
What you are trying to do is not technically impossible, but rather difficult and not recommended as can be seen by reading comments and answers to this question.
As your error message points out your def won't quite happen in the context you "probably" expect and you certainly won't be able to "freely use erb features" while defining the method (since your context is completely different from what you expect).
The "railsy" way to do this is adding a helper or using a partial, but both come with their drawbacks. .erb files (as most templating-languages) do not "factor" well. If you want to factor things somewhat more freely you should look at the fortitude gem which provides what is basically a ruby-DSL for html which factors pretty easily. However, that is a rather drastic change from what you are probably used to.
If you really want to define a method inside an .erb-file then you would have to do it entirely within a single pair of <% ... %> brackets where you will have only access to your params, not your context. You would have to return what is basically a String in order to be able to use it in <%= ... %> and pay a hell of a lot of attention to escaping rules for everything to make it through. This is most probably more trouble than it is worth (but easy enough to do in fortitude :-).

How to concatenate #something inside a render function in a template

I have this function in my template:
<%= for {element, id} <- Enum.with_index(MyProject.PageView.Recursion.buildElements(#header_linkNumber),1) do %>
<%= render FabricaASA.ComponentView, #header_linkType,
button_id: "#{id}",
button_mainStyle: #header_mainStyle
%>
<% end %>
Now I would like to concatenate, on my right side, #header_mainStyle + id so that from other template, for each created element, I could pass: header_mainStyle1, header_mainStyle2,...header_mainStyleN
Also, on the left side, where I have button_mainStyle: I would like to concatenate #header_linkType + _mainStyle: so that I could dynamically change it to, link_mainStyle: or button_mainStyle:
Up to now I wasn't able to do it properly...
I'm afraid you are doing something wrong if you need such thing. Maybe there's a simpler solution...
Anyway: since some version of Phoenix (I'm sorry I don't know which one precisely, maybe 1.0?), #-variables are stored in #conn.assigns map and you can access them by name there. In older versions, these variables were macros and this kind of magic did not work.
So you can try to put this into the controller:
def index(conn, _params) do
render conn, "index.html", [var1: "var1"]
end
and this into the page template:
<p>var1: <%= #var1 %></p>
<p>assigns:</p>
<%= for i <- 1..10 do %>
<p>var<%= i %>:<p>
<pre><%=
varname = "var#{i}" |> String.to_atom
inspect(#conn.assigns[varname]) %>
</pre>
<% end %>
...you will see var1 to var10 bindings (screenshot: http://postimg.org/image/4b4790cjz/). But it's little bit black magic and probably wrong approach.

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 refactoring issue

The following view code generates a series of links with totals (as expected):
<% #jobs.group_by(&:employer_name).sort.each do |employer, jobs| %>
<%= link_to employer, jobs_path() %> <%= "(#{jobs.length})" %>
<% end %>
However, when I refactor the view's code and move the logic to a helper, the code doesn't work as expect.
view:
<%= employer_filter(#jobs_clone) %>
helper:
def employer_filter(jobs)
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
link_to employer, jobs_path()
end
end
The following output is generated:
<Job:0x10342e628>#<Job:0x10342e588>#<Job:0x10342e2e0>Employer A#<Job:0x10342e1c8>Employer B#<Job:0x10342e0d8>Employer C#<Job:0x10342ded0>Employer D#
What am I not understanding? At first blush, the code seems to be equivalent.
In the first example, it is directly outputting to erb, in the second example it is returning the result of that method.
Try this:
def employer_filter(jobs)
employer_filter = ""
jobs.group_by(&:employer_name).sort.each do |employer,jobs|
employer_filter += link_to(employer, jobs_path())
end
employer_filter
end
Then call it like this in the view:
raw(employer_filter(jobs))
Also note the use of "raw". Once you move generation of a string out of the template you need to tell rails that you don't want it html escaped.
For extra credit, you could use the "inject" command instead of explicitly building the string, but I am lazy and wanted to give you what I know would work w/o testing.
This syntax worked as I hoped it would:
def employer_filter(jobs_clone)
jobs_clone.group_by(&:employer_name).sort.collect { |group,items|
link_to( group, jobs_path() ) + " (#{items.length})"
}.join(' | ').html_safe
end

Resources