How do I use optional attributes to select articles? - ruby-on-rails

#routes.rb
get "/:year(/:month(/:day))(/:genre)" => "archives#index", :constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
#archives_controller.rb
def index
#articles = Article.all(params[:year, :month, :day, :genre],:order => "created_at DESC")
I want to be able to get the articles by year, or year and month, or year and month and day, with genre optionally on the end of any of those. Can I do this in one statement or do I need if blocks? Also I want to be able to get all the articles in a specific genre, but I think I need to do that in a separate action? Thanks!
UPDATE
I ended up using the metawhere gem plus a method to build my date:
def index
date_builder
#articles = Article.where(:created_at.matches % #date, :genre.matches % #genre).order("created_at DESC")
respond_to do |format|
format.json { render :json => #articles }
format.xml { render :xml => #articles }
format.html
end
end
def date_builder
#date = ""
#date += params[:year] if !(params[:year].nil?)
#date += "-" + params[:month] if !(params[:month].nil?)
#date += "-" + params[:day] if !(params[:day].nil?)
#date += "%"
end
This reduced the number of sql calls to one and make everything look pretty. Thanks for your help!

You can use has_scope gem to define some scopes to be applied to your collection if there're matching params in the request. You can find has_scope here.
Other alternative could be the meta-where and meta-search gems. You can find an explanation on how to use them here.

You could do something like this:
#articles = Article.where(:year => params[:year], :month => params[:month]).order("created_at DESC")
#articles = #articles.where(:day => params[:day]) if params[:day]
#articles = #articles.where(:genre => params[:genre]) if params[:genre]

Related

How to get acts on taggable working

I am new to ruby on rails (and programming) and this is probably a really stupid question. I am using Rails 3.2 and trying to use acts_as_taggable_on to generate tags on articles and to have those tags show on article index and show pages as a clickable links.
I have tags clickable on both the article show and index pages, but the links just go back to the index page and don't sort according to the tag name. I have scoured the Internet and pieced together the code below from various sources, but I am clearly missing something.
Any help is greatly appreciated, as I have exhausted my seemingly limited knowledge! Thanks.
Here is what I have:
class ArticlesController < ApplicationController
def tagged
#articles = Article.all(:order => 'created_at DESC')
#tags = Article.tag_counts_on(:tags)
#tagged_articles = Article.tagged_with(params[:tags])
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #articles }
end
end
def index
#articles = Article.paginate :page => params[:page], :per_page => 3
#tags = Article.tag_counts_on(:tags)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #articles }
end
end
module ArticlesHelper
include ActsAsTaggableOn::TagsHelper
end
class Article < ActiveRecord::Base
acts_as_ordered_taggable
acts_as_ordered_taggable_on :tags, :location, :about
attr_accessible :tag_list
scope :by_join_date, order("created_at DESC")
end
article/index.html.erb
<% tag_cloud(#tags, %w(tag1 tag2 tag3 tag4)) do |tag| %>
<%= link_to tag.name, articles_path(:id => tag.name) %>
<% end %>
article/show.html.erb
<%= raw #article.tags.map { |tag| link_to tag.name, articles_path(:tag_id => tag) }.join(" | ") %>
routes.rb file snippet
authenticated :user do
root :to => 'home#index'
end
devise_for :users
resources :users, :only => [:show, :index]
resources :images
resources :articles
You can run 'rake routes' from the terminal to see all your paths. Here your tags are pointing at articles_path, which you will see routes to the index action in the articles controller ("articles#index")
You could create another route in your routes.rb file, something like:
match 'articles/tags' => 'articles#tagged', :as => :tagged
Place it above others in the routes file if you want it to take precedence, and remember you can always run 'rake routes' in the terminal to see how the routes are interpreted.
see http://guides.rubyonrails.org/routing.html#naming-routes for more info (maybe read the whole thing)
Another (probably better) option would be to combine your desired functionality into the index action using params, e.g. .../articles?tagged=true. Then you could use logic to define the #articles variable in the index controller based on params[:tagged]. A simple example might be
def index
if params[:tagged]
#articles = Article.all(:order => 'created_at DESC')
else
Article.paginate :page => params[:page], :per_page => 3
end
#tags = Article.tag_counts_on(:tags)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #articles }
end
end
This is called DRYing up your code (for Don't Repeat Yourself); it would save you the need for code duplication in the articles#tagged action, which would make it easier to understand and maintain your code base.
Hope that helps.

Add quotes to an ID in json with rails

my current json output is "id":3,"name":"test", and I need the 3 to be "3".
How would I go about doing this in rails?
def search
#tags = Tag.where("name like ?", "%#{params[:q]}%")
respond_to do |format|
format.json { render :json => #tags.to_json(:only => [:id, :name]) }
end
end
Sergio's solution will work. However, if you're doing this in more than one place, I would suggest overriding Rails' built in function as_json.
In your Tag model:
def as_json(options={})
options[:id] = #id.to_s
super(options)
end
Your controller method will remain unchanged. This is untested, but should work.
Something like this:
format.json do
tags = #tags.to_json(:only => [:id, :name])
render :json => tags.map{|t| t['id'] = t['id'].to_s; t}
end
This was the only solution that worked for me:
def search
#tags = Tag.where("name like ?", "%#{params[:q]}%")
respond_to do |format|
format.json { render :json => #tags.map {|t| {:id => t.id.to_s, :name => t.name }} }
end
end

sunspot with i18n framework in rails 3.2

I'm using sunpost gem for search in my rails project.
I have now two languages in my app:
I18n.default_locale = :en
LANGUAGES = [
['English',
'en'],
["Español".html_safe, 'es']
]
I have in my post.rb model, a language attribute that contains the value "es" for spanish language or value "en" for english language.
I have in posts_controller in index action the next method:
def index
#search = Post.solr_search do |s|
s.fulltext params[:search]
s.keywords params[:search]
s.order_by :created_at, :desc
s.paginate :page => params[:page], :per_page => 20
end
#posts = #search.results
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
format.js
end
end
I get the current language with I18n.locale.to_s I get with this code "es" or "en"
My question is: How can I only show the results for the language currently in use by user in my website?
Thank you very much!
It would be very helpful if you could post the searchable block in the post model. But until then, I will take a stab at it.
Your Post model should look something like the following:
class Post < ActiveRecord::Base
searchable do
...
string :language
...
end
end
Where you are indexing the language the post is written/stored in.
Then your controller you use the language field as a filter. It should look like:
def index
#search = Post.search do |s|
s.keywords params[:search]
s.with(:language, I18n.locale.to_s) if I18n.locale.present?
s.order_by :created_at, :desc
s.paginate :page => params[:page], :per_page => 20
end
#posts = #search.results
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
format.js
end
end
and there you have it!

Thinking Sphinx: search across multiple models: best practices?

I want to add a jquery autocomplete with categories.
The request will search across multiples models (Forum topics, news, users...) with Thinking Sphinx
So in controller, I think it will look like that
def autocomplete
#news = Actu.search(params[:term]).map {|g| {:label => g.title, :category => "Actualités", :id => g.id}}
#topics = Topic.search(params[:term]).map {|g| {:label => g.title, :category => "Topics", :id => g.id}}
#anotherModel = ...
respond_to do |format|
format.js { render :json => #news+#topics+#anotherModel }
end
end
That working, but what do you think about these practice ?
You can try this awesome syntax
ThinkingSphinx.search 'pancakes', :classes => [Article, Comment]
Read more at http://freelancing-god.github.com/ts/en/searching.html
You can search across all indexed models in your application:
ThinkingSphinx.search(params[:term])
Then you can define for each model method, say autocomplete_json, that returns hash.
So, your action
def autocomplete
render :json => ThinkingSphinx.search(params[:term]).map(&:autocomplete_json)
end

Changing number of returned search results with Rails and Ajax

I recently changed the pagination with will_paginate in my Rails (2.3.4) app to use Ajax for the pagination and records per page. The pagination was done using the method described here: http://github.com/mislav/will_paginate/wiki/Ajax-pagination
I'm using this code in my view:
Records per page: <%= select_tag :per_page, options_for_select([4,8,24,100,500], #per_page.to_i), :onchange => remote_function(:url => users_path, :method => :get, :with => "'per_page=' + this.getValue()") %>
This works fine if I'm not viewing search results. But if I do a search and them attempt to change the records-per-page, my search results are lost and all records are returned. I'm pretty sure this is due to the url I'm calling in my remote_function, but I don't know how to fix it.
This is the code in my controller:
def index
#search = User.search(params[:search])
#search.order||="ascend_by_last_name"
if #search.count > 0
#users = #search.paginate(:page => params[:page], :per_page => users_per_page )
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #users }
format.csv { send_data #users.to_csv }
format.js {
render :update do |page|
# 'page.replace' will replace full "results" block...works for this example
# 'page.replace_html' will replace "results" inner html...useful elsewhere
page.replace 'results', :partial => 'userview'
end
}
end
else
flash[:notice] = "Sorry, your search didn't return any results."
redirect_to users_path
end
end
Any help would be appreciated! Thanks.
You can append the per_page param to the end of the current query string.
:with => "'#{request.query_string}&per_page=' + this.getValue()"
This assumes there is a current query string, which could cause issue.

Resources