How to generate Excel file with passing params from AJAX search? - ruby-on-rails

I'm performing AJAX search in my Rails application. Here is code from controller:
def show
#website = Website.find(params[:id])
if (current_user.id != #website.user_id)
redirect_to root_path
flash[:notice] = 'You are not owner!'
end
if params[:report] && params[:report][:start_date] && params[:report][:end_date]
#performance_reports = #website.performance_reports.where("created_at between ? and ?", params[:report][:start_date].to_date, params[:report][:end_date].to_date)
else
#performance_reports = #website.performance_reports
end
but when I'm trying to generate Excel document it alway goes to branch without params, because there are no params in URL.
One man reccomend me to use this post. I tried to implement it, but couldn't.
I don't understand this post enough, I just can't get where data is passing(spreadsheet gem)
Here is code:
def export
#website = Website.last
#data = #website.performance_reports
report = Spreadsheet::Workbook.new
spreadsheet = StringIO.new
contruct_body(spreadsheet, #data)
report.write spreadsheet
send_data spreadsheet.string, :filename => "yourfile.xls", :type => "application/vnd.ms-excel"
end
and it gives me error:
undefined method `contruct_body'
Code from view:
<%= form_tag( url_for, :method => :get, :id => "report") do%>
...show action posted above...
<% end %>
<%= link_to export_path do%>
<b>Export</b>
<% end %>
...working code without AJAX...
<%= link_to url_for(request.parameters.merge({:format => :xls})) do%>
<b>Export</b>
<% end %>
Please tell me where is my mistake or suggest ano

For the first problem, you need to show the view code and the path ajax is taking. Give us more information how the excel is being called.
For the second issue, you need to define that method. Specify how you will populate the spreadsheet with the data. Here is the guide. https://github.com/zdavatz/spreadsheet/blob/master/GUIDE.txt
== Writing is easy
As before, make sure you have Spreadsheet required and the client_encoding
set. Then make a new Workbook:
book = Spreadsheet::Workbook.new
Add a Worksheet and you're good to go:
sheet1 = book.create_worksheet
This will create a Worksheet with the Name "Worksheet1". If you prefer another
name, you may do either of the following:
sheet2 = book.create_worksheet :name => 'My Second Worksheet'
sheet1.name = 'My First Worksheet'
Now, add data to the Worksheet, using either Worksheet#[]=,
Worksheet#update_row, or work directly on Row using any of the Array-Methods
that modify an Array in place:
sheet1.row(0).concat %w{Name Country Acknowlegement}
sheet1[1,0] = 'Japan'
row = sheet1.row(1)
row.push 'Creator of Ruby'
row.unshift 'Yukihiro Matsumoto'
sheet1.row(2).replace [ 'Daniel J. Berger', 'U.S.A.',
'Author of original code for Spreadsheet::Excel' ]
sheet1.row(3).push 'Charles Lowe', 'Author of the ruby-ole Library'
sheet1.row(3).insert 1, 'Unknown'
sheet1.update_row 4, 'Hannes Wyss', 'Switzerland', 'Author'
Add some Formatting for flavour:
sheet1.row(0).height = 18
format = Spreadsheet::Format.new :color => :blue,
:weight => :bold,
:size => 18
sheet1.row(0).default_format = format
bold = Spreadsheet::Format.new :weight => :bold
4.times do |x| sheet1.row(x + 1).set_format(0, bold) end
And finally, write the Excel File:
book.write '/path/to/output/excel-file.xls'

Related

DRY and Elegant way to represent all search combinations without growing exponentially?

Right now I can search the following
1) leaving_from location
2) going_to location
3) leaving_from &
going_to location
if params[:leaving_from].present? && params[:going_to].present?
#flights = Flight.where(:source => params[:leaving_from]).where(:destination => params[:going_to])
elsif params[:leaving_from].present?
#flights = Flight.where(:source => params[:leaving_from])
elsif params[:going_to].present?
#flights = Flight.where(:destination => params[:going_to])
end
Is there a dry way to represent this code above? Basically its a for search function compromised of 2 drop down search boxes. One for leaving from location and another for going to location. With the option of narrowing it down by both locations or just one location.
It works fine now but it isn't very scalable. If I added more search parameters say price and time, it would grow exponentially in order to be able to represent all the states.
For example if I added price my new combinations would be
1) leaving_from location
2) going_to location
3) leaving_from &
going_to location
4) price
5) leaving_from location & price
6) going_to location & price
7) leaving_from location & going_to location & price
I need help to figure out a better way to represent this, or else it would make my controller incredibly bloated.
EDIT FORM CODE --
=form_tag '/flights', :method => :get
%h4
Leaving From:
=select_tag 'leaving_from', content_tag(:option,'select one...',:value=>"")+options_for_select(#flights_source, 'source'), { :class => 'form-control' }
%h4
Going To:
=select_tag 'going_to', content_tag(:option,'select one...',:value=>"")+options_for_select(#flights_destination, 'destination'), { :class => 'form-control' }
%h4=submit_tag "Search", :name => nil, :class => 'btn btn-success btn-md btn-block'
In place of using leaving_from or going_to use source and destination instead and Move all the required parameters under a key, e.g., this solution will work for any no. of keys
'required' => { 'source' => value, 'destination' => value, 'price' => value }
Now in the controller define this method in private
def get_flights(params)
possible_combination = []
conditions = {}
key_array = params['required'].keys
1.upto(key_array.length) { |i| possible_combination + key_array.combination(i).to_a }
possible_combination.reverse.each do |comb|
if comb.collect{ |key| params['required'][key].present? }.inject(:&)
comb.map { |key| conditions[key] = params['required'][key] }
break
end
end
Flight.where(conditions)
end
Call this method from any action
#flights = get_flights(params)
Hope this works! Its an overall idea to make this thing dynamic, you can refactor the code according to your need!
First things first: your code does not do what you think it does, since there is no way for it to execute the third if (every time the third if is true, the first if is as well). On to your question:
#flights = Flight
#flights = #flights.where(:source => params[:leaving_from]) if params[:leaving_from].present?
#flights = #flights.where(:destination => params[:going_to]) if params[:going_to].present?
Or
conditions = {}
conditions[:source] = params[:leaving_from] if params[:leaving_from].present?
conditions[:destination] = params[:going_to] if params[:going_to].present?
#flights = Flight.where(conditions)
How about using ransack which adds your rails to search function very easily.
You just write below, if you use ransack.
# View (Search Form)
<%= search_form_for #q do |f| %>
From: <%= f.text_field :leaving_from_cont %>
To : <%= f.text_field :going_to_cont %>
Price:
<%= f.text_field :price_gteq %> 〜 <%= f.text_field :price_lteq %>
<%= f.submit %>
<% end %>
# Controller
def index
#q = Flight.ransack(parmas[:q])
#flights = #q.result(distinct: true)
end
If a user don't input any fields, ransack don't use the non-input fields value. It means don't add WHERE conditions in DB.
column_name_cont means contain (Like in DB).
column_name_eq means equal (== in DB).
column_name_gteq means greater than equal (<= in DB).
column_name_lteq means less than equal (>= in DB).
etc...
Also you can sort the search result easily by using sort_link methods of ransack.
Please look in ransack.
I was not able to get #RSB's code to work but I was able to use his example to create a method that did work. I call the below code in my action.
#flights = get_flights(search_params)
The search_params method is as follows:
def search_params
params.permit(:leaving_from, :going_to)
params_hash = {'required' => { 'source' => params[:leaving_from], 'destination' => params[:going_to]}}
end
And finally the get_flights method is:
def get_flights(params)
possible_combination = []
conditions = {}
key_array = params['required'].keys
possible_combination = (possible_combination + key_array.combination(key_array.length).to_a).flatten
possible_combination.each do |comb|
conditions[comb] = params['required'][comb] if params['required'][comb].present?
end
Flight.where(conditions)
end
I am still pretty new to ruby and rails so any feedback or suggestions for improvements would be greatly welcome. Thanks!

How to I make a drop down beside a search box that searches the specific field selected in rails?

Okay so im new to this site but this is what I have:
Report.rb
def self.search(search)
if search
where('JOBLETTER_CD_NUMBER LIKE ? AND DATE LIKE? AND CUST LIKE ?', "%#{search}%")
else
scoped
end
end
end
index.html.erb
select_tag "search", options_for_select([ "Job Letter and CD #", "Date", "Cust", "Job", "Date shipped", "Date billed", "Billed by" ], params[:search])
form_tag reports_path, :method => 'get' do
text_field_tag :search, params[:search], :class=> "form-search", :align => "right"
<%= submit_tag "Search", :JOBLETTER_CD_NUMBER => nil, :class => "btn btn-success", :align => "right"
reports controller
def index
#report = Report.paginate(:per_page => 1, :page => params[:page])
#report = Report.search(params[:search]).paginate(:per_page => 1, :page => params[:page])
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #views }
end
end
The only field it will search is the Job Letter and CD # field I need it to allow me to search whatever is selected in the drop down box. Btw I am using bootstrap fro js and css functions.
Your query has 3 placeholders ? but passed only one argument "#{search}" - if you run it like that, what you really should be getting is an exceptions stating
ActiveRecord::PreparedStatementInvalid: wrong number of bind variables (1 for 3) ...
Also, your select_tag is outside the form, so it won't be passed to the controller at all. If you move it into the form, you'd have to rename (e.g. to column) it since the name search is already used by the text field. Then you could pass both the column and the search parameters to your search function to construct the query.
HOWEVER, this is not safe, since nothing prevents a user to pass in any other column by manipulating the post request, and since you can't use placeholders for column names, there's a danger of SQL injection as well.
There are many solutions out there to construct searches, no need to reinvent the wheel. Take a look at the ransack gem. Here's a recent Railscast on how to use it.

Trouble configuring search field to pass searched value into function

I'm pretty new to Ruby on Rails, forms lately have been giving me all kinds of trouble so your help and support here would be very valuable.
In my home.html.erb file I have this code:
<%= form_tag({:controller => "pages", :action => "search"}, :method => "get", :class => "grabTweets") do %>
<%= text_field_tag(:tweets)%>
<% end %>
I'm trying to take the value that the user enters into that search field and pass it into a function called grabTweets that contains the following code:
def grabTweets(mySearch)
#tweet = Twitter.search(mySearch + "[pic] "+" instagr.am/i/", :rpp => 2, :result_type => "recent").map do |status|
#tweet = "#{status.text}" #class = string
urls = URI::extract(#tweet, "http") #returns an array of strings
end
end
The my form code in home.html.erb renders correctly but doesn't communicate with the grabTweets function. As much Googling and tinkering as I do, I can't seem to figure out the answer. The code in home.html.erb adds this to the end of my localhost URL: /assets?utf8=✓&tweets=google, so the search isn't being routed correctly.
Thanks in advance!
There are two things: first you need to make sure that your url gets routed to the write controller
match '/search', :to "pages#search"
ought to do it.
Then in your PagesController's search action, call
grabTweets(params[:tweets])
and do something interesting with the results. Obviously if grabTweets isn't an instance method on your controller then you'd need to adjust how you're calling it.
Your url is fine. For search, you should have the arguments passed in as GET params (as you currently are doing)
The problem is just how you're accessing the search argument. Instead of trying to read an argument in the function like this:
def grabTweets(mySearch)
#tweet = Twitter.search(mySearch + "[pic] "+" instagr.am/i/", :rpp => 2, :result_type => "recent").map do |status|
#tweet = "#{status.text}" #class = string
urls = URI::extract(#tweet, "http") #returns an array of strings
end
end
Read off the params hash:
def grabTweets
#tweet = Twitter.search(params[:tweets] + "[pic] "+" instagr.am/i/", :rpp => 2, :result_type => "recent").map do |status|
#tweet = "#{status.text}" #class = string
urls = URI::extract(#tweet, "http") #returns an array of strings
end
end
And this is Ruby. Please don't CamelCase variables.

Rails accessing variable in email template

I'm still on the rather steep side of the Rails learning curve, so please pardon the rather simplistic nature of this question, but Google's just not proving very helpful.
So, my issue is this. I have a controller that is calling a mailer. (Code snippet below)
The problem I can't seem to get passed is that no matter how I try to access the values in the rhtml page, I either get errors or nothing at all.
This is the controller snippet (#person is working just fine. #item is what's not working)
if params[:id] == 'username'
item_value = #user[:login]
elsif params[:id] == 'password'
item_value = #user[:new_password]
end
#item = { 'name' => params[:id], 'val' => item_value }
ApplicantMailer.deliver_forgot(#person.email, #person, #item)
This is the mailer method snippet:
def forgot(recipient, person, item, sent_at = Time.now)
#subject = 'Site Password Retrieval'
#body['person'] = person
#body['item'] = item
#recipients = 'rdavis#localhost'
#from = CONTACT_EMAIL
#sent_on = sent_at
#headers = {}
logger.debug #body.to_yaml
end
This is the rhtml snippet:
Dear <%= #person.first_name %>,
You are receiving this email because you or someone else has used the lost <%= #item[:name] %> page from the login page.
Your <%= #item[:name] %> for your account is: <%= #item[:val] %>
So, like I said, when I try to access the values for the #item, if I use #item.name it throws a missing method error and if I use the version listed above, it doesn't show anything.
I know I'm working with a hash & thought that trying to access the keys like I showed here was the right way. Obviously, I'm missing something here.
Can someone point me in the right direction, please? Thanks!
You are using strings when creating your hash and symbols when accessing it.
You want the following:
#item = { :name => params[:id], :val => item_value }
Which is distinct from:
#item = { 'name' => params[:id], 'val' => item_value }
You can try this in irb with the following.
hash = {"a" => "val1", :a => "val2", "b" => "val3"}
hash["a"] => "val1"
hash[:a] => "val2"
hash["b"] => "val3"
hash[:b] => nil

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