ActiveRecord to_json include association in :methods - ruby-on-rails

I am converting a User object to json via:
user.to_json :methods => :new_cookies
the new_cookies method is:
cookies.all :include => :fortune, :conditions => {:opened => false}
This embed the cookies inside the user json object, but I want fortune to be embedded inside the cookie object as well. I passed inside :include => :fortune but that doesn't that work.
Is this possible?
Models:
class User < ActiveRecord::Base
has_many :cookies
has_many :fortunes, :through => :cookies
def new_cookies
cookies.all :include => :fortune, :conditions => {:opened => false}
end
end
class Cookie < ActiveRecord::Base
belongs_to :user
belongs_to :fortune
end
class Fortune < ActiveRecord::Base
serialize :rstatuses
serialize :genders
has_many :cookies
has_many :users, :through => :cookies
end

I am not sure that the :includes => :fortune option works as you expect (or perhaps at all) -- near the end of this section of the current Rails guides it mentions this option for finder methods other than .all.
I assume it works similarly to the new Active Relation query interface, e.g. Cookies.include(:fortunes).where(:opened => false) -- in this case, Rails "eager loads" the related records, meaning fortunes are fetched as part of the query for cookies. This is a performance enhancement, but doesn't otherwise change the behavior of Rails.
As I noted in the comments, I think as_json will do what you want -- it defines what is and is not part of the object when serialized using to_json. You specify methods that should be called in addition to (or to exclude) the methods of the data-backed object itself, for example, your new_cookies method.
In this example, I have added as_json to User (which gets Cookie) and also to Cookie in hopes that each cookie will nest its fortunes in its JSON. See below for an alternative.
class User < ActiveRecord::Base
has_many :cookies
has_many :fortunes, :through => :cookies
def as_json(option = {})
super(:methods => :new_cookies)
end
def new_cookies
cookies.all :conditions => {:opened => false}
end
end
class Cookie < ActiveRecord::Base
belongs_to :user
belongs_to :fortune
def as_json(options = {})
super(:methods => :cookie_fortune) # or perhaps just :fortune
end
def cookie_fortune
self.fortune
end
end
In a case where I was writing an API and didn't need to reflect the nested relationships between objects in the JSON, in the controller, I used something like
respond_to do |format|
format.html
format.json { render json: { :foo => #foo, :bar => #bar }}
end
to produce parallel nodes (objects) in the JSON.

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.

Include both joined tables in Rails JSON API call

I am getting the following Active Record Association error when trying to join two tables (with a polymorphic relationship) and include all data from both tables in a JSON API response:
Association named 'categories' was not found; perhaps you misspelled it?
Here is the controller action that I am trying to call:
def index
#items = Item.includes(:categories)
respond_to do |format|
format.json { render :json => #items.to_json }
end
end
And here are the two models that I am trying to join:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :items, :as => :linkable
end
class Item < ActiveRecord::Base
attr_accessible :due_date, :linkable_id, :linkable_type, ...
belongs_to :linkable, :polymorphic => true, :counter_cache => true
end
Specifically, I want to return each Item in the database along with its Category. I have tried everything that I can think of. Any help will be greatly appreciated.
Have you tried :
def index
#items = Item.includes(:linkable).where(:linkable_type => 'Category')
respond_to do |format|
format.json { render :json => #items.to_json(include: :linkable) }
end
end
The name of your association is actually :linkable for the Item model, and not :categories (especially because it's a belongs_to so it would be :category).

Has_many through - One Sided Uniqueness in the database

I have a three models:
class Feed < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :keywords, :through => :filters, :uniq => true
end
class Filter < ActiveRecord::Base
belongs_to :feed
belongs_to :keyword
validates_uniqueness_of :keyword_id, :scope => :feed_id
end
class Keyword < ActiveRecord::Base
has_many :filters, :dependent => :destroy
has_many :feeds, :through => :filters
end
What I want is to have only unique entries in the database for keywords. For example, if two feeds both have a keyword 'hello', there should be two filters (one for each feed) both pointing to the same keyword.
What I am having trouble with is the controller code. Perhaps I am looking for too simple a solution, but I figure there must be an easy way to do this. This is what I have in my create action so far:
def create
#feed = Feed.find(params[:feed_id])
#keyword = #feed.keywords.create(params[:keyword])
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end
With this controller code, the previous example would result in a duplicate keyword in the database, one for each feed/filter. Is there a straight-forward solution to this or do I need to do a check beforehand to see if there is already a keyword and in that case just create the filter?
Use a dynamic finder find_or_create_by :
def create
#feed = Feed.find(params[:feed_id])
#keyword = Keyword.find_or_create_by_keyword(params[:keyword]) # I assume here that you have a column 'keyword' in your 'keywords' table
#feed.keywords << #keyword unless #feed.keywords.all.include?(#keyword)
redirect_to feed_keywords_path(#feed), notice: 'Keyword added successfully.'
end

Where should I put the code?

I have the models User and StoredItem:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
end
class StoredItem < ActiveRecord::Base
belongs_to :user
named_scope :lookup, lambda { |id| { :conditions => ['qid = ?', id]}}
end
I need to have two methods to add and remove the items to StoredItem for current user. I put this code to User model:
class UserData < ActiveRecord::Base
has_many :stored_items, :dependent => :destroy
def save_item(params)
if(!self.stored_items.lookup(params[:qid]).exists?)
item = self.stored_items.new(:sid => params[:qid],
:name => params[:qti],
:url => params[:qur],
:group_id => params[:title],
:rating => Integer(params[:rating]))
item.save
end
end
def remove_item(qid)
item = self.stored_items.lookup(qid).first()
item.destroy
end
end
So here is the StoredItem controller:
def save_item
#user = UserData.find_by_login(session[:cuser])
#user.save_item(params)
# ...
end
Is it good architectural decision or it will be better to put this code to StoredItem model and pass the current user into it?
This is a good architectural decision. You need to keep it in the user since the User is the owner of the StoredItem. The user is responsible for its stored items, not the other way around.

How can I handle a folder-like model structure?

I have this Rails model: (Parameters removed for clarity)
class Folder < ActiveRecord::Base
belongs_to :parent, :class_name => :folder
has_many :children, :class_name => :folder
end
I want this model to be used like a file system folder. How do I have to configure the routes and the controller to make this possible?
1) As for the model: check out acts_as_tree
2) As for the routes: do something like
map.folder '/folders/*path', :controller => 'folders', :action => 'show'
and in the FoldersController,
def show
# params[:path] contains an array of folder names
#folder = Folder.root
params[:path].each |name|
#folder = #folder.children.find_by_name(name) or raise ActiveRecord::RecordNotFound
end
# there you go, #folder contains the folder identified by the path
end

Resources