global variable in application controller? - ruby-on-rails

I grap the first country with
#country = Country.find(1)
Then i my head navigation i make this loop to get the right tags:
%ul.thumbnails
- #country.tags.find_each(:conditions => "active_house = true") do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_region_houses_path(#country, a.name), :class => 'btn-nav-drop'}
This works fine. But the navigation is global so i created a method in application_controller like this:
helper_method :tags
def tags
#country = Country.find(1)
#tags = #country.tags.find_each(:conditions => "active_house = true")
end
And in the navigation view:
%ul.thumbnails
- tags do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_houses_path(#country, a.name), :class => 'btn-nav-drop '}
But i get a error message "no block given (yield)"
Thanks..remco

Well, this is nothing about global variable which should be avoided whenever possible.
Your problem is in this line
tag_country_houses_path(#country, a.name)
There is NO #country exists in View.
You may wonder why. The reason is helper can't pass instance variables to View, different from controller.
What your helper all did is to return an array object #tags. The value of this object is available in view, but not instance variable #tags, neither #country.
The fix? Use something to replace #country. If the association is country has_many tags, you can do it like this:
tag_country_houses_path(a.country, a.name)
If not, you can set a method in tag model to get the country.
And you can even use some includes to make query more efficient but that is another story.
Also, your helper can be simplified without assigning any variables.
def tags
Country.find(1).find_each(:conditions => "active_house = true")
end

find_each accepts a block that will be yielded. Because you write find_each in helper without the block, so it will throw an error. You have two solutions.
Solution1: You can just use find to return an array.
def tags
Country.find(1).tags.find(:all, :conditions => "active_house = true")
end
in your view:
- tags.each do |t|
.........
Solution2: you can pass the block to your helper.
def tags
Country.find(1).tags.find_each(:conditions => "active_house = true") do |t|
yield t
end
end
in your view.
- tags do |t|
.........
If you have not many many records, just use solution1.

Related

Rails - how to return just text of an ActiveRecord record

I have a model of cities and one of the attributes is the city 'name'.
I've done a helper method to return 'n' number of cities and the implementation is below:
helper method:
def list_cities(start, stop)
cities = City.find(:all, order: "name asc", limit: stop-start, select: "name")
cities.each do |city|
"<li> #{city.name} </li>"
end
end
view code:
<%= list_cities(1,22) %>
However, it returns the following in the view:
[#<City name: "Abilene">]
How do I get just the text of the city name and get rid of the rest of the query?
The problem is that your method list_cities does not return the string you think it does. It just returns an array of city objects because that's what the method each does. It looks like you're mixing controller and view logic into a helper. What I'd do is:
Set a #cities instance variable in the controller:
#cities = City.find(:all, order: "name asc", limit: stop-start, select: "name")
In the view:
<% #cities.each do |city| %>
<li><%= city.name %></li>
<% end %>
This way you keep your controller and view logic separated.
What Erez answered is the best way to approach this issue. But I'd like to share what you can do to your method to achieve what you want with minimal changes. You need to change your helper to the following
def list_cities(start, stop)
cities = City.all(order: 'name ASC', limit: stop - start, select: 'name')
cities.map { |city| "<li> #{city.name} </li>" }.join.html_safe
end
The other changes are just small refactors to reduce code. Without knowing the rails version you're using, I didn't change the query but if you're using 3.2, you should have access to pluck which is faster since it won't create ActiveRecord objects.
Try:
def list_cities(start, stop)
content = ''
cities = City.find(:all, order: "name asc", limit: stop-start, select: "name")
cities.each do |city|
content << "<li> #{city.name} </li>\n"
end
content
end

Using case statement in a helper

I haven't used case statements before and was wondering how to do the following.
I have a number of news pages, each having posts relevant to department, so the pages
/tynewyddnews
/woodsidenews
/outreachnews
/sandpipernews
On my home page I am grabbing the latest post from each of these and displaying them.
Post Model
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
tynewydd_posts for example is a scope
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
So if i am reading a post on the home page from tynewydd and want to create a link to /tynewyddnews or i am reading a post from woodside and want to link_to /woodside I have been advised that a case statement may help, but I am unsure on what to use as parameters, so my attempt so far is
def public_news
case Post.top_posts
when :tynewydd_posts == 'Ty Newydd'
link_to('...Read more', tynewyddnews_path)
when :woodside_posts == 'Woodside'
link_to('...Read More', woodsidenews_path)
end
end
And then in my view i can call the helper
<%= public_news %>
obviously a miserable attempt, firstly in examples I have seen a variable is being set when the case is being set? if someone could give some advice on how to achieve this it would be much appreciated
Try this variant:
def public_news
path = case
when Post.tynewydd_posts then tynewyddnews_path
when Post.woodside_posts then woodsidenews_path
...
else default_path end
end
link_to('...Read More', path)
end
when expression will fire up when expression is evaluated to true

Change pagination dynamically in ActiveAdmin or solutions for printing

I'm new to Activeadmin and rails and I need some help.
I have a model that is paginated and I want to allow the user to change the pagination value or disable it completely, so it can print (to a printer) all the records (or filtered ones) for instance.
I know I can set the pagination using #per_page in :before_filter, but I can't figure out how I can change this value during execution.
To solve the problem of needing to show all the unpaginated records I defined a custom page, but in this page the filter or scope don't work so it's kind of useless.
How can I create a Print button in Active Admin?
This is a workaround to do it, I know it is not the best solution but it works ! :)
This is the app/admin/mymodel.rb file
ActiveAdmin.register MyModel do
before_filter :paginate
#other code
controller do
def paginate
#per_page = params[:pagination] unless params[:pagination].blank?
end
end
index do
panel "Pagination" do
render partial: "paginate", locals: {resource: "mymodels"}
end
#other code
end
#other code
end
And for the app/views/admin/articles/paginate.html.haml
#pagination_form
= form_tag do
= label_tag :pagination, "Number of " + resource + " per page : "
= text_field_tag :pagination
= submit_tag "Filter"
:javascript
$("#pagination_form form").submit(function(e){
e.preventDefault();
window.location = "/admin/#{resource}?pagination=" + $("#pagination").val();
})
Hoping that my answer can people with the same problem :)
I found a solution and I'm answering my own question for someone who has the same problem.
It may not be the best solution but it works, if someone has a better way please share:
ActiveAdmin.register mymodel do
before_filter :apply_pagination
# other code
index :download_links => false, :as => :table, :default => true do
if params[:pag].blank?
div link_to(I18n.t("text_for_the_link"), 'mymodel?pag=1', :class => "class_for_link")
else
div link_to(I18n.t("print.print"), 'mymodel', :class => "class_for_link")
end
# other code
end
controller do
def apply_pagination
if params[:pag].blank?
#per_page = 50
else
#per_page = 99999999
end
# other code
end
end
I found out you can define this by registering the following line on the resource:
ActiveAdmin.register MyModel do
config.per_page = [20, 50, 100, 200]
end
It automatically adds a select box in the index pagination with the preset values given in the array.

Dynamic url_path in each loop

I have a city page with a jQuery content carousel. The content of the carousel is filed by a each loop.
CityController
#events = #city.events.find_all_by_hot(true)
#activities = #city.activities.find_all_by_hot(true)
#sights = #city.sights.find_all_by_hot(true)
#hot = #events + #activities + #sights
Class city
has_many: events
end
class events
belongs_to :city
has_many :attachments, :as => :attachable
accepts_nested_attributes_for :attachments
end
Activities and sights models are the same
City view content slider:
#hot.each do |a|
a.attachments.each do |a|
= image_tag(a.file.url, :height =>"325px", :width =>"650px" ), url_path
I want to generate links (url_path) in my each loop...how can I realize this? It cannot place the url_path of the routes because they are dymanic based on which attachment (image) is loaded.
Although your syntax of image_tag is incorrect you can try this
#hot.each do |hot|
hot.attachments.each do |a|
link_to polymorphic_path(a.attachable) do
image_tag(a.file.url, :height => "325px", :width => "650px")
end
end
end
If I understand your problem correctly. Also check out the polymorphic_path helper, which is what you need.
Am I right that the link should point to the a which can be any of events, activities, sights? as
#hot = #events + #activities + #sights
I would try creating a special controller action in the CityController
def hottie
#duck = Kernel.const_get(params[:type]).find_by_id(params[:id])
redirect_to #duck
end
then add something like
match 'hottie/:type/:id' => 'city#hottie', as: 'hot'
which should give you a path helper that you can use as this:
<%=link_to("Open", hot_path(a.class.to_s, a.id)) %>
Addition: this is of course a bit dirty and needs some security things considered (e.g. limit it to show only special types). You could also consider moving the three classes Event, Activities and Sights into a Object Hierarchy using STI; that should eliminate the need of passing the type in the request.

Searching in Ruby on Rails - How do I search on each word entered and not the exact string?

I have built a blog application w/ ruby on rails and I am trying to implement a search feature. The blog application allows for users to tag posts. The tags are created in their own table and belong_to :post. When a tag is created, so is a record in the tag table where the name of the tag is tag_name and associated by post_id. Tags are strings.
I am trying to allow a user to search for any word tag_name in any order. Here is what I mean. Lets say a particular post has a tag that is 'ruby code controller'. In my current search feature, that tag will be found if the user searches for 'ruby', 'ruby code', or 'ruby code controller'. It will not be found if the user types in 'ruby controller'.
Essentially what I am saying is that I would like each word entered in the search to be searched for, not necessarily the 'string' that is entered into the search.
I have been experimenting with providing multiple textfields to allow the user to type in multiple words, and also have been playing around with the code below, but can't seem to accomplish the above. I am new to ruby and rails so sorry if this is an obvious question and prior to installing a gem or plugin I thought I would check to see if there was a simple fix. Here is my code:
View: /views/tags/index.html.erb
<% form_tag tags_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search], :class => "textfield-search" %>
<%= submit_tag "Search", :name => nil, :class => "search-button" %>
</p>
<% end %>
TagsController
def index
#tags = Tag.search(params[:search]).paginate :page => params[:page], :per_page => 5
#tagsearch = Tag.search(params[:search])
#tag_counts = Tag.count(:group => :tag_name,
:order => 'count_all DESC', :limit => 100)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #tags }
end
end
Tag Model
class Tag < ActiveRecord::Base
belongs_to :post
validates_length_of :tag_name, :maximum=>42
validates_presence_of :tag_name
def self.search(search)
if search
find(:all, :order => "created_at DESC", :conditions => ['tag_name LIKE ?', "%#{search}%"])
else
find(:all, :order => "created_at DESC")
end
end
end
If I read your problem correctly, you want to return a row if the tag names for the row matches one of the words passed in the query string.
You can rewrite your search method as follows:
def self.search(search)
all :conditions => (search ? { :tag_name => search.split} : [])
end
If you need partial matching then do the following:
def self.search(str)
return [] if str.blank?
cond_text = str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values = str.split.map{|w| "%#{w}%"}
all(:conditions => (str ? [cond_text, *cond_values] : []))
end
Edit 1
If you want pass multiple search strings then:
def self.search(*args)
return [] if args.blank?
cond_text, cond_values = [], []
args.each do |str|
next if str.blank?
cond_text << "( %s )" % str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values.concat(str.split.map{|w| "%#{w}%"})
end
all :conditions => [cond_text.join(" AND "), *cond_values]
end
Now you can make calls such as:
Tag.search("Ruby On Rails")
Tag.search("Ruby On Rails", "Houston")
Tag.search("Ruby On Rails", "Houston", "TX")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah", ....) # n parameters
Caveat:
The wild card LIKE searches are not very efficient(as they don't use the index). You should consider using Sphinx (via ThinkingSphinx) OR Solr(via SunSpot) if you have lot of data.
You can try to set up ferret, or if you are really bend on just using rails, try this:
# Break the search string into words
words = params[:search].blank? ? [] : params[:search].split(' ')
conditions = [[]] # Why this way? You'll know soon
words.each do |word|
conditions[0] << ["tag_name LIKE ?"]
conditions << "%#{word}%"
end
conditions[0] = conditions.first.join(" OR ") # Converts condition string to include " OR " easily ;-)
# Proceed to find using `:conditions => conditions` in your find
hope this helps =)
Sounds like you need a full text search. The best search integration right now is with Sphinx and the Thinking_Sphinx plugin. I have used it on several projects and it's super easy to setup.
You do need to install sphinx on your host so if you are using a shared host that could present some issues.
You could also use full text search in a MyISAM MySQL database, but performance on that is pretty poor.
Once you have your sphinx installed you just put what you want to index in your model and call model.search. The results will be a list of model objects. It supports will_paginate as well.
I'd suggest looking at Searchlogic if you don't want to use a separate fulltext search engine (Ferret, Sphinx, etc). It makes simple searches extremely easy, although you may not want to use it in a public facing area without lots of testing.
Also check out the Railscast on it: http://railscasts.com/episodes/176-searchlogic
1.You can do some coding in your controller post as such:-
<pre>
def show
#post = Post.find(params[:id])
#tag_counts = Tag.count(:group => :name, :order => 'updated_at DESC', :limit => 10)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
</pre>
2.Now make some changes in your view file:-
<pre>
<b>Tags:</b>
<%= join_tags(#post) %>
<%unless #tag_counts.nil?%>
<% #tag_counts.each do |tag_name, tag_count| %>
<tr><td><%= link_to(tag_name, posts_path(:name => tag_name)) %></td>
<td>(<%=tag_count%>)</td>
</tr><% end %>
<%end%>
</pre>
3. And one important thing is that there should be many to many relationship between tags and post.

Resources