'distance' field gets dropped when using Geokit with acts_as_mappable :through - ruby-on-rails

We know that the distance field gets dropped when using the Geokit gem in Rails with acts_as_mappable :through model class. I wonder if there's a way to work around this to get the distance field back. I tried to follow the monkey-patching example over here:
http://www.sobyteme.com/news/2010/05/13/computers/2010/06/25/geokit-acts_as_mappable-through-with-distance-attribute/
but it didn't work for me.

Well, Steve's suggestion over on his site was accurate, I was missing calling sort_by_distance_from after doing the find. So credit goes to him for this answer.
I'm on Rails v3.0.7. Here's my code:
class Office < ActiveRecord::Base
has_many :users
acts_as_mappable :default_units => :miles,
:default_formula => :sphere,
:lat_column_name => :latitude,
:lng_column_name => :longitude
end
class User < ActiveRecord::Base
belongs_to :office
acts_as_mappable :through => :office
end
users_controller.rb:
# Monkey patching to include the 'distance' attribute
module Geokit
module Mappable
def to_lat_lng
return self if instance_of?(Geokit::LatLng) || instance_of?(Geokit::GeoLoc)
return LatLng.new(self.office.send(self.office.class.lat_column_name),
self.office.send(self.office.class.lng_column_name)) if self.class.respond_to?(:acts_as_mappable)
nil
end
end
end
class UsersController < ApplicationController
def location
#lat = params[:lat].to_f
#long = params[:long].to_f
#origin = [#lat, #long]
#users = User.find(:all,
:origin => #origin,
:conditions => "distance < 3")
# We have to add this to get the 'distance' field
#users.sort_by_distance_from(#origin)
respond_to do |format|
format.html
format.xml { render :xml => #users.to_xml(:methods => :distance)}
format.json { render :json => #users.to_json(:methods => :distance)}
end
end
...
end

Related

Rails render object in json with model associations

I'm trying to render the user model along with the posts model, but I'm having trouble figuring out what the syntax for it would be
class Post < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
end
Controller
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(only: [:topic, :location, :latitude, :longitude],)
end
Output:
[{"topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}]
Desired Output:
[{"user_name":"Randy","topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}
The user model has #user.user_name which is the one I need for each post.
How do I render the user associated with each post?
I hope this works
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(:only => [:topic, :location, :latitude, :longitude], :include => {:user => {:only => :user_name}})
end
Please check the apidock for more details.

Ruby On Rails Pagination and delete :through association

I am running into a strange situation, considering the following models:
class Collection < ActiveRecord::Base
attr_accessible :name, :season, :year
has_many :collection_items_assocs
has_many :items, :through => :collection_items_assocs
end
class Item < ActiveRecord::Base
attr_accessible :name, :reference, :item_type_id
has_many :pictures
has_one :item_type
end
class CollectionItemsAssoc < ActiveRecord::Base
attr_accessible :collection_id, :item_id
belongs_to :item
belongs_to :collection
end
I can successfully retrieve Items associated to a Collection with the following code:
# GET /collections/1
# GET /collections/1.json
def show
#collection = Collection.find(params[:id])
#collection.items = Collection.find(params[:id]).items
respond_to do |format|
format.json { render json: #collection.to_json(:include => {:items => #collection}) }
end
end
But when I try to include pagination (for items) like that
# GET /collections/1
# GET /collections/1.json
def show
#collection = Collection.find(params[:id])
**#collection.items = Collection.find(params[:id]).items.paginate(:page => params[:page],:per_page =>1)**
respond_to do |format|
format.json { render json: #collection.to_json(:include => {:items => #collection}) }
end
end
It works for the following call
/retailapp/collections/1?format=json&**page=1**
Then if I call
/retailapp/collections/1?format=json&**page=2**
the records in the association table CollectionItemsAssoc are deleted
I really don't get it
Thanks for your help
The problem is the code to fetch the items
#collection.items = Collection.find(params[:id]).items
it assigned the fetched items to current collection object.
you need to change the response to support the pagination on associate objects
  def show
   #collection = Collection.find(params[:id])
respond_to do |format|
format.json {
json_hash = #collection.as_json
json_hash[:items] = #collection.items.paginate(:page => params[:page],:per_page =>1).as_json
render json: json_hash.to_json
}
end
Additionally you can overwrite to_json method inside Collection model.

Rails Active Record Query and JSON

I have the following models in my Rails application:
class Transaction
belongs_to :category
has_one :group, :through => :category
class Category
belongs_to :group
has_many :transactions
class Group
has_many :categories
has_many :transactions, :through => :category
In my controller is the following:
#transactions = Transaction.includes(:category, :group).group("groups.id").sum("amount")
respond_to do |format|
format.json{
render :json => JSON.generate(#transactions.as_json(:include => [:category, :group]))
}
end
This produces the following json (A):
{"1":2000,"3":5000,"2":1000}
However, my goal is to produce something like this (B):
[{"group_id":1,"amount":2000},{"group_id":3,"amount":5000},{"group_id":2,"amount":1000}]
Any help on how I can go from A to B would be greatly appreciated.
Try:
def user_transactions
#transactions = Transaction.includes(:category, :group).group("groups.id").sum("amount")
respond_to do |format|
format.html
format.json do
render :json => custom_json_for(#transactions)
end
end
end
private
def custom_json_for(value)
list = value.map do |k,v|
{ :group_id => k,
:amount=> v
}
end
list.to_json
end
If you're doing a lot a JSON-serialization, I would recommend you to take a look at ActiveModelSerializers:
https://github.com/rails-api/active_model_serializers

Rails Delegate Not working as expected

I have the following classes:
class VideoChannel < ActiveRecord::Base
#Associations
belongs_to :video_playlist, :dependent => :destroy
VideoChannel.video_playlist_name
delegate :name, :id, :list_type, :list_id, :manual, :to => :video_playlist, :prefix => true
#validations
validates_presence_of :name
#After Functions
def after_create
video_playlist = VideoPlaylist.new(:name => self.name,
:list_type => "VideoChannel",
:list_id => self.id)
video_playlist.save
end
And :
class VideoPlaylist < ActiveRecord::Base
belongs_to :list, :polymorphic => true
has_many :video_channels, :dependent => :destroy
delegate :name, :id, :description, :to => :video_channel, :prefix => true
end
I'm trying to use the Rails Delegate function to create a link in the VideoChannel page that allows me to to link to the Video Playlist and edit the contents there. So the association is there and You can currently edit the playlists by going through the playlists section but we want to combine them. I can't seem to figure this out. Im also very new to Rails, still working through the guides etc.
Edit: Here's the view code
<%= link_to '<span class="pen icon"></span>Edit',
content_url(:controller =>"video_playlists", :id => channel.video_playlist_id, :action => "edit"),
:class => "button right" %>
Here are teh relevant pieces of the controllers:
class VideoChannelsController < ApplicationController
# GET /videochannels
# GET /videochannels.xml
def index
#video_channels = VideoChannel.roots(:order => 'order_num')
#video_channels_parents = #video_channels.group_by {:parent_id}
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #video_channels }
end
end
# GET /videochannels/1
# GET /videochannels/1.xml
def show
#video_channel = VideoChannel.find(params[:id], :order => 'order_num')
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #video_channel }
end
end
end
class VideoPlaylistsController < ApplicationController
# GET /video_playlists
# GET /video_playlists.xml
def index
if !params[:with].nil?
#video_playlists = VideoPlaylist.find(:all, :conditions => {:list_type => 'VideoShow'})
else
#video_playlists = VideoPlaylist.find(:all, :conditions => {:list_type => 'Section'})
end
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #video_playlists }
end
end
# GET /video_playlists/1
# GET /video_playlists/1.xml
def show
#video_playlist = VideoPlaylist.find(params[:id], :include => [{:video_video_playlists => :video}, {:videos => :asset}, {:videos => :content_image}])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #video_playlist }
end
end
end
Where did the line
VideoChannel.video_playlist_name
Come from? What's it doing? You're also calling a method on the class not an instance (sort of - Ruby isn't quite like this, but it's enough to explain).
Anyway:
Delegate is really for avoiding lots of train wreck code like this:
fred.jim.bill.xxx
You've said that they belong to each other - the relationships look like they're the wrong way round. Why are you creating the parent from inside the child? How are you going to have many video channels belonging to a given playlist?
I think you need to look at build and the relationship names. To gat past me maybe misunderstanding your model, lets have switch to a product that has many stock items:
class Product < ActiveRecord::Base
has_many :stock_items
end
class StockItem < ActiveRecord::Base
belongs_to :product
end
This means that stock_item will have a column product_id.
So, say you're creating a product you'd do something like:
product.stock_items.build # :whatever the params are required
This automatically sets the id's for you and means you don't have to set id's. Then when you do product.save it will save all the related stock items too.
And in the view for this toy model, then if you were displaying one of the stock items, you'd use delegate to show the name of the product in the view without having to do lost of stock_item.product.name (for example).
I hope this helps.

Filter on "include" - Rails 3

these are my associations:
class Activity < ActiveRecord::Base
has_many :infos, :dependent => :destroy
has_many :events, :dependent => :destroy
accepts_nested_attributes_for :infos
end
class Event < ActiveRecord::Base
belongs_to :activity
has_many :infos, :through => :activity
end
class Info < ActiveRecord::Base
has_one :language
belongs_to :activity
end
Now i can get an XML with all the events and their infos using:
#events = Event.all
respond_to do |format|
format.xml { render :xml => #events.to_xml(:include => [:infos ]) }
end
The problem is that i get the infos from all the language.
Is it possible to use a filter (as "where info.language.id==1"), so that only the language1 infos are displayed in the XML for each event?
Thanks
UPDATE :
Hi Mike, thanks for your answer.
Unfortunately i'm getting this error:
undefined method `eq' for nil:NilClass
Rails.root: /Users/abramo/village
Application Trace | Framework Trace |
Full Trace
app/controllers/events_controller.rb:29:in
block (2 levels) in locale'
app/controllers/events_controller.rb:28:in
locale'
and lines 28,29 are the last line of my locale method:
def locale
#events = Event.joins(:infos => :language).where("languages.id = 2")
respond_to do |format|
format.xml { render :xml => #events.to_xml(:include => [:infos ]) }
end
end
I really don't understand... what object is Nil?
Event.joins(:infos => :language).where("languages.id = 1")

Resources