Using case statement in a helper - ruby-on-rails

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

Related

Rails 3, change or fill submitted form values in controller

I have two independent "component" models, contacts and articles. Contacts come in many types (producers, exporters etc...).
In the new article form I have a dropdown selector with contacts (id and title) and want to store the selected value and text in the article table.
In the new article form view:
<%= f.select :producer_id, options_for_select(producers, #article.producer_id) %>
That works and producer_id is stored in article table.
That's clear and logical to me, but in some cases I also need to store the selected contact's title in producer_title.
I have read many different options like "do it in model, before save", or "do it in controller", and I have done it inside controller.
Article controller (only part from update):
#cont_name is producer title from Contacts
def update
params[:article][:producer_title] = Contact.where(id: params[:article][:producer_id]).pluck(:cont_name).first
end
This works, but is it the best-practices approach to this problem?
Also, why I can't get it to work if I change the params[producer_id] part to use: id: params[:producer_id] ?
Best regards and thanks.
How about something like the following instead:
def edit
#article = Article.find(params[:id])
#producers = Contact.where(type: "producer") # or however you distinguish between producers and other contacts
end
Then in your form change it to:
f.select :producer_id, options_from_collection_for_select(#producers, "cont_name")
# you might need to do (#producers, "cont_name", "cont_name"), can't quite remember
Then your update action will be much simpler:
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
...
else
...
end
end
The reason :id => params[:producer_id] doesn't work is that params is a nested hash, so something like:
params = { :article => { :producer_id => 34, :title => "Cool Article" }, :another_key => { :key => :value } }
So to access the producer_id you first have to retrieve the article hash, otherwise it will only look through the first set of keys which include article and another_key in the example above, but don't include producer_id.

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.

Next and Previous records

In my show.html.erb file I have two links for next and previous like so
<%= link_to("Previous Post", #question.previous) if #question.previous %>
<%= link_to("Next Post", #question.next) if #question.next %>
.previous and .next are methods in my model which finds either the current id + 1 or - 1
like so in my model
def previous
Question.find_by_id(id - 1, :select => 'id')
end
def next
Question.find_by_id(id + 1, :select => 'id')
end
But rather than incrementing and decrementing blindly as I might have deleted a "Question" so i'll get an error, could I find the next/previous "Question" from the scope of the query the show action was accessed from?
Have you tried something like this?
def previous
Question.limit(1).order("id DESC").where("id < ?", id)
end
def next
Question.limit(1).order("id DESC").where("id > ?", id)
end
Try Act as Ordered.
https://github.com/imedo/acts_as_ordered

Rails/ajax - whitespace and request.raw_post

I am starting to learn Ajax with rails.
I have a catalog index page with a text_field_tag querying db if it finds similar "section" results.
Index.html.erb
<h1>Catalogs</h1>
<label>Search by Section:</label>
<%=text_field_tag :section %>
<%= observe_field(:section,
:frequency=> 0.1,
:update=> "article_list",
:url=>{ :action => :get_article_list }) %>
<div id="article_list"></div>
Catalogs_controller.rb
def index
end
def get_article_list
#section = request.raw_post.split(/&/).first
#catalogList = "<ol>"
Catalog.find(:all, :conditions => ["section = ?", #section]).each do |catalog|
#catalogList += "<li>" + catalog.title + "</li>"
end
#catalogList += "</ol>"
render :text => #catalogList
end
Question:
request.raw_post renders something like:
xml&authenticity_token=tgtxV3knlPvrJqT9qazs4BIcKYeFy2hGDIrQxVUTvFM%3D
so I use
request.raw_post.split(/&/).first
to get the section query ("xml"). It works, however how can I do if the query have a whitespace. (like "Open Source") In fact, I have Open Source sections in my db, but request.raw_post.split(/&/).first renders Open%20Source. How can I manage this? Did I have to use a full text search engine to achieve it or there is another way?
Thanks a lot for your explanation!
Look over your logs, in them you will see the post and the params being passed. You should not need to do your own query-string splitting. You should be able to use params[:section] to get the post data.
As your comment implies, there's something missing. Your observe_field needs to tell the Rails helper what to do. Check out: http://apidock.com/rails/ActionView/Helpers/PrototypeHelper/observe_field. Anyhow, you'll want to do something like:
observe_field(... # lots of parameters
:with => 'section'
)
And that should give you params[:section].

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