I have implemented query on distant lat long and create virtual field in query like this
user = User.all
if longitude.present? && latitude.present? && fulladdress.present?
distantQuery = "(3959 * acos( cos( radians("+latitude+") ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians("+longitude+")) + sin( radians("+latitude +")) * sin(radians(latitude)))) < " + miles
user = User.select("*, (3959 * acos(cos(radians("+latitude+")) * cos( radians(latitude)) * cos(radians(longitude) - radians("+longitude+")) + sin(radians(" +latitude +")) * sin(radians(latitude)))) as distant").where(distantQuery)
end
when my if condition run then distant is coming other wise it will not coming
<% #result.each do |r| %>
<tr>
<td>
<%= r.distant.present %>
</td>
</tr>
now in view my distant is come form query then it will show result and when it will not come then it show me error
undefined method `distant' for #<User:0x000000092a6558>
I have implement "present" in view
<% if r.distant.present? %>
<%= r.distant.to_i %>
<% end %>
but still it show me error, What will i use to check if distant is coming then it will show and other wise not ?
this should work:
<% if r.respond_to?(:distant) %>
however this is too much logic in the view, i suggest you refactor some of your code to have very simple and safe methods in your view
Try this
<%= r.try(:distant) %>
I would use another approach:
user = if longitude.present? && latitude.present? && fulladdress.present?
distantQuery = QUERY_BODY_HERE
User.select(SELECT_QUERY_HERE).where(distantQuery)
else
User.all
end
unless user.respond_to(:distant)
class << user
def distant ; nil ; end
end
end
The advantage of this approach is that you have consistent User class, responding to #distant correctly regardless of whether it was calculated.
Related
I have a students collection result set and I need following.
Display names should be resolved following these rules: If there are no other students in the collection with the same first name, their display
name should be just their first name.
If there are multiple students in the collection with the same first name, their display name should be their first name followed by a space and their last initial(e.g. “John Smith” would resolve to “John S”)..
Try this
#results.each do |object|
displayname = (#results.select { |filter_object| filter_object.first_name == object.first_name }.count > 0) ? object.first_name : object.first_name + " " + object.last_name.initial
end
here's an example, this might not specifically be what you need (this kinda sounds like homework), but hopefully it gives you an idea.
# in Student model
attr_accessor :display_name
# in controller
students = Student.all
students.each do |student|
if students.count { |s| s.first_name == student.first_name } > 1
student.display_name = s.first_name
else
student.display_name = "#{student.first_name} #{student.last_name[0].upcase}"
end
end
# in view
<% students.each do |student| %>
<%= student.display_name %>
<% end %>
First find out duplicate first names.
dup_first_names = Student.select(:first_name).group(:first_name).group(:first_name).having("COUNT(*) > 1").uniq.pluck(:first_name)
Then for each student check whether the first name is in the dup_first_names array.
Student.all.each do |s|
if dup_first_names.include?(s.first_name)
puts "#{s.first_name} #{s.last_name.first}"
else
puts "#{s.first_name}"
end
end
In my rails application I need to call a method defined in one table from another controller. There are two tables named coordinates and tweets. The condition for the tweets table is decided by the coordinates table. In my final view I need to display the attributes of the tweets table for which the condition is decided by the coordinates table.
My Coordinates table code
class Coordinates<ActiveRecord::Base
def self.query()
a = Coordinates.where("city=?", params[:show])
b = a.first
if a.count == 1
latitude = b.latitude
longitude= b.longitude
if(latitude=0 && longitude=0) then
return sql="Select * from tweets where tweet_text LIKE '%text%' AND user_loc LIKE '%show%' order by id desc"
else if (latitude!=0 && longitude!=0)
min_lat = latitude - 1.0
max_lat = latitude + 1.0
min_lng = longitude - 1.0
max_lng = longitude + 1.0
return sql = "Select * from tweets where tweet_text LIKE '%text%' AND ( ((longitude BETWEEN min_lng and max_lng) AND (latitude BETWEEN min_lat and max_lat)) OR (user_loc LIKE '%show%') ) order by id desc"
else
return sql="Select * from tweets where tweet_text LIKE '%text%'"
end
end
end
My tweets table
class Tweets<ActiveRecord::Base
attr_accessible :id, :tweet_created_at, :tweet_id, :tweet_text, :tweet_source, :user_id, :user_name, :user_sc_name, :user_loc, :user_img, :longitude, :latitude, :place, :country
end
I need to call the query definition from my tweets_controller so that it decides what query to fetch from the tweets table and display in the final view . But the params function is not working in the model.rb file . I want something like this
My tweets_controller.rb
class TweetsController<ApplicationController
def index
Coordinates.query()
end
end
My final view code
<%= #tweets.each do |tweets| %>
<ul>
<li><%= tweets.id %></li>
<li><%= tweets.tweet_created_at %></li>
<li><%= tweets.tweet_source %></li>
<li><%= tweets.tweet_text %></li>
<li><%= tweets.user_id %></li>
<li><%= tweets.user_name %></li>
<li><%= tweets.user_sc_name %></li>
<li><%= tweets.user_loc %></li>
<li><%= tweets.user_img %></li>
<li><%= tweets.longitude %></li>
<li><%= tweets.latitude %></li>
<li><%= tweets.place %></li>
<li><%= tweets.country %></li>
</ul>
<% end %>
I am not able to call the query function defined in coordinates table from tweetscontroller. Also I tried using helper methods but that does not seem to help and sounds very complicated. Anybody kindly help me with this
Actually, I see a few issues with your code.
First off: use meaningful names. You are retrieving tweets pertaining to a certain city. Secondly, in your query method, most of what you are trying to do is retrieving tweets, which should be in the tweet model.
What your code does wrong:
it just builds the sql?
it searches for tweets containing the fixed text %text%, I am assuming that should be a given search-term
it searches for a given user-location %show%, I am assuming that should be the city-name (your params[:show]
What I would suggest:
finding tweets should be in Tweet model
use more smaller methods
for simplicity I assume your search-term is params[:text]
So I would write your code as follows:
class TweetsController < ApplicationController
def index
city = params[:show]
search_term = params[:text]
city_coordinates = Coordinates.where('city=?', city)
#tweets = if city_coordinates.count == 1 && city_coordinates.first.valid_location?
Tweet.for_coordinate(city_coordinates.first)
else
Tweet.for_user_location(city)
end
#tweets = #tweets.where("tweet_text like ?", "%#{search_term}%")
end
end
Do not build the sql yourself, let activerecord do that for you. Also: where is lazy, so you can easily chain them.
Then in your Coordinate model add
class Coordinate < ActiveRecord::Base
def valid_location?
self.latitude != 0 && self.longitude != 0
end
end
and in your Tweet model
class Tweet < ActiveRecord::Base
def self.for_coordinate(coordinate)
bbox = { min_lat: coordinate.latitude - 1.0, max_lat: coordinate.latitude + 1.0,
min_lng: coordinate.latitude - 1.0, max_lng: coordinate.latitude + 1.0
}
Tweet.where("(longitude BETWEEN ? and ?) AND (latitude BETWEEN ? and ?)) OR (user_loc LIKE ?)",
bbox[:min_lng], bbox[:max_lng], bbox[:min_lat], bbox[:max_lat], "%#{coordinate.city}%")
end
def self.for_user_location(city)
Tweet.where("user_loc like ?", "%#{city}%")
end
end
In your self.query method in model, set an argument like this :
class Coordinates<ActiveRecord::Base
def self.query(something)
a = Coordinates.where("city=?", something)
b = a.first
if a.count == 1
latitude = b.latitude
longitude= b.longitude
if(latitude=0 && longitude=0) then
return sql="Select * from tweets where tweet_text LIKE '%text%' AND user_loc LIKE '%show%' order by id desc"
else if (latitude!=0 && longitude!=0)
min_lat = latitude - 1.0
max_lat = latitude + 1.0
min_lng = longitude - 1.0
max_lng = longitude + 1.0
return sql = "Select * from tweets where tweet_text LIKE '%text%' AND ( ((longitude BETWEEN min_lng and max_lng) AND (latitude BETWEEN min_lat and max_lat)) OR (user_loc LIKE '%show%') ) order by id desc"
else
return sql="Select * from tweets where tweet_text LIKE '%text%'"
end
end
end
In your controller, pass your params[:show] as as parameter :
class TweetsController<ApplicationController
def index
#tweets = Coordinates.query(params[:show])
end
end
Now it should work. Thanks
You call Coordinates.query() but you don't actually store the return value.
Try this:
#tweets = Coordinates.query()
I am new to ruby on rails and can't really write methods yet....
I am getting an error when uploading an image which doesn't contain exif data.
I need this method to allow for both imgfile and imgfile.nil. How would I either define a default value or ignore this code if imfile.nil?
# Image geolocation
def get_image_loc
imgfile = EXIFR::JPEG.new(image.queued_for_write[:original].path)
return unless imgfile
lat = imgfile.exif[0].gps_latitude[0].to_f + (imgfile.exif[0].gps_latitude[1].to_f / 60) + (imgfile.exif[0].gps_latitude[2].to_f / 3600)
lng = imgfile.exif[0].gps_longitude[0].to_f + (imgfile.exif[0].gps_longitude[1].to_f / 60) + (imgfile.exif[0].gps_longitude[2].to_f / 3600)
lat = lat * -1 if imgfile.exif[0].gps_latitude_ref == "S" # (N is +, S is -)
lng = lng * -1 if imgfile.exif[0].gps_longitude_ref == "W" # (W is -, E is +)
self.img_loc_lat = lat # imgfile.gps_latitude
self.img_loc_lng = lng # imgfile.gps_longitude
end
end
here is the part which calls this method in my show.erb file
<% unless #pin.img_loc_lat.blank?
# bespoke code to load a map if the location information is available
# Google Static Maps for now
# Look to upgrade to Google Maps API 3 - there seem to be a few gems available for it
%>
<p> <br />
<img src="http://maps.googleapis.com/maps/api/staticmap?center=<%= #pin.img_loc_lat %>,<%= #pin.img_loc_lng %>&zoom=13&size=600x300&maptype=roadmap&markers=color:green%7Clabel:P%7C<%= #pin.img_loc_lat %>,<%= #pin.img_loc_lng %>&sensor=false">
</p>
<% end %>
My git
https://github.com/nathan-wallace/dcphotogrid-app.git
You can check if img_loc_lat is nil:
<% unless #pin.img_loc_lat.nil? %>
If there is a possiblity of img_loc_lat being a string or nil, you can also convert it to a string and check if it's empty:
<% unless #pin.img_loc_lat.to_s.empty? %>
You could rewrite the html block as a helper and then call it as a method with .try()
#instance.try(:method_name)
if #instance is blank then the method won't be called.
I have the following method in my model:
def self.set_bad_recommedation_size(rating_set)
bad = Rating.where(rating_set: rating_set).where(label: 'Bad').count
total = Rating.where(rating_set: rating_set).count
percentage_bad = (bad.to_f/total.to_f * 100)
return bad, total, percentage_bad
end
How do I call the variable bad, total, percentage_bad in my view.
What I want:
<%= "#{Model.set_bad_recommedation_size(rating_set).bad}/#{Model.set_bad_recommedation_size(rating_set).total"%>
You're better off doing:
<% bad, total, percentage_bad = Model.set_bad_recommedation_size(rating_set) %>
<%= "#{bad}/#{total}" %>
That way you're not calling the method multiple times.
I would add an intermediate helper so that your view reads better
<%= bad_recommendation_ratio(result_set) %>
Application.helper
def bad_recommendation_ratio(result_set)
bad, total = Model.set_bad_recommedation_size(rating_set)
"#{bad}/#{total}"
end
I have created a loop, to calculate a total rating of a record. To do this I am first looping through all the child records (ratings), extracting the rating from each row, adding it to the total and then outputting the total.
<% total = 0 %>
<% for ratings in #post.ratings %>
<% total = (total + ratings.rating) %>
<% end %>
<%= total %>
My question is, simply, Is this the rails way?
It achieves the desired result, although needs 5 lines to do so. I am worried I am bring old habits from other languages into my rails project, and I am hoping someone could clarify if there is an easier way.
The following, preferably in the controller, will do it succinctly:
#rating = #post.ratings.sum { &:rating }
If that seems cryptic, you might prefer
#rating = #post.ratings.inject(0) { |sum, p| sum + p.rating }
Note, however, that this will fail if any of the ratings are null, so you might want:
#rating = #post.ratings.inject(0) { |sum, p| sum + (p.rating || 0) }
You should generally keep logic out of your views. I would put that code in a helper or a controller, and the call a method to calculate the total
Put the following in your controller, then you just need to use #rating in your view:
total = 0
#rating = #post.ratings.each { |r| total += r.rating }
Or you could move it into the Post model and do something like:
def self.total_rating
total = 0
ratings.each { |r| total += r.rating }
total
end
and then simply call #post.total_rating