Cannot access attr_accessor defined variables - ruby-on-rails

I am using Thinking Sphinx to run searches and I get the appropriate ActiveRecord Models fine. The problem is, I want to create an appropriate link path and text on each model, then send the info to the browser in the form of JSON, via AJAX. I am using the following to build those link attributes:
In the controller:
class FindController < ApplicationController
def tag_results
#results = ThinkingSphinx.search(params[:terms])
#results.each do |result|
result.build_ajax_response
end
respond_to do |format|
format.html
format.json { render :json => #results }
end
end
end
In the model:
class TaggedItem < ActiveRecord::Base
attr_accessible :name
attr_accessor :search_link, :search_text
def build_ajax_response
self.search_link = Rails.application.routes.url_helpers.tagged_item_path(self.id)
self.search_text = self.name
end
end
The resulting json object doesn't have either of the search_* attributes listed, much less have a value for them. I've tried using #search_link as well as just search_link in the build_ajax_response method.
Am I doing this wrong? Could there be something else interfering?

Rails' default to_json doesn't know about those extra non active record attributes you've added. The easiest possible thing is probably to specify them as extra methods to include:
format.json { render :json => #results.to_json(:methods => [:search_link, :search_text]) }

Related

Add virtual methods with parameters to as_json input

I got this model:
class Post
def rating_by(ip_address, user = nil)
if user
ratings.where("ratings.user_id = ?", user.id).first
else
ratings.where("ratings.ip_address = ?", ip_address).first
end
end
end
As you might notice, I allow ratings by both users and visitors.
I'd like to output posts as json with an additional user_rating attribute.
This is my current controller:
#posts = Post.trending(10)
respond_to do |format|
format.html
format.json { render json: #posts.to_json }
end
Of course this won't show it, but I'd like to know if there's a possibility of using to_json's :methods option and specify parameters as well, something like:
#posts.to_json(extra: {user_rating: "rating_by(#{request.remote_ip}, #{#current_user.id})"})
So that I end up with something like:
[{ id: 54, title: "Foo", user_rating: 8 }]
Other suggestions are very welcome!
I commented on your question, but I thought I would add some detail.
From Active Model Serializers' page :
class PersonSerializer < ActiveModel::Serializer
attributes :first_name, :last_name, :full_name
def full_name
"#{object.first_name} #{object.last_name}"
end
end
So you could define a user_rating parameter in your serializer.
Moreover, Active Model Serializers allows the use of a meta
render json: #posts, meta: {total: 10}
So you could simply computer user_rating and send it with meta.

Rails: overriding as_json for dynamic value -- is there a smarter way?

I want to output a list of affiliate links, each tagged to identify the current user. It would be simple in HTML, but we're writing an API, so the output is JSON.
I have it working, but it seems overly complicated. Is this the best approach?
My model, AffiliateLink contains a field (the raw HTML of the link) that I'll transform and output on the fly by adding a token. I have a model method that produces the replacement -- it is non-trivial because we use multiple affiliates and each has a special transformation rule that this method knows about:
def link_with_token(user_token)
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
To get my correct link html in JSON I have done these things:
add attr_accessor :link_html to model
add an instance method to set the new accessor
...
def set_link_html(token)
self.link_html = link_with_tracking_token(token)
end
override as_json in the model, replacing the original html_code with link_html
...
def as_json(options = {})
super(:methods => :link_html, :except => :html_code)
end
iterate over the collection returned in the controller method to do the transformation
...
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
#links.each do |link|
link.set_link_html(account_tracking_token)
end
render json: #links
end
end
end
This seems like a lot of stuff to do just to get my teensy-weensy transformation done. Helpful suggestions (relating to this problem and not to other aspects of the code, which is in flux now) are welcome.
1) A quick solution to your problem (as demonstrated here):
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links.to_json(:account_tracking_token => account_tracking_token)
end
end
end
AffiliateLink.rb
# I advocate reverse_merge so passed-in options overwrite defaults when option
# keys match.
def as_json(options = {})
json = super(options.reverse_merge(:except => :html_code))
json[:link_with_token] = link_with_token(options[:account_tracking_token])
json
end
2) A more hardcore solution, if you're really writing an API:
See this article describing your problem.
See the gem that the authors made as a solution.
See this railscast on using the gem.
3) And lastly, the convenient solution. If you have a convenient model relation, this is clean:
Pretending AffiliateLink belongs_to :user. And assuming user_token is an accessible attribute of User.
AffiliateLink.rb
# have access to user.user_token via relation
def link_with_token
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
def as_json(options = {})
super(options.reverse_merge(:methods => :link_with_token, :except => :html_code))
end
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links
end
end
end

Nil class error

I want to display the related products to a certain need, just the picture and the name nothing more depending on the categorie
here is mu controller
class RelatedneedsController < ApplicationController
def index
#relatedneeds = RelatedNeed.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #relatedneeds }
end
end
def show
s1 = '#need.category.name'
s2 = '#relatedneed.category.name'
if s1.eql?(s2)
#relatedneed = relatedneed.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #need }
end
end
end
def get_name
#relatedneed.name
end
end
and this my model
class Relatedneed
include Mongoid::Document
include Mongoid::Paperclip
mapping do
indexes :name
end
field :name, type: String
belongs_to :category
belongs_to :user
end
and this is show.haml file
%h1
%b= #need.name
#container{:style => "width:1000px"}
#desc{:style => "height:400px;width:400px;float:left;"}
=image_tag #relatedneed.photo.url(:normal)
this is my index.haml file
%h1= #relatedneed.get_name
#container{:style => "width:1000px"}
#desc{:style => "background-color:#EEEEEE;height:400px;width:400px;float:left;"}
= link_to "Check Need", new_need_path
I don't know if their is something missing and i get this error
NoMethodError in RelatedneedsController#index
undefined method `key?' for nil:NilClass
Your index.haml has #relatedneed.get_name but you have not set #relatedneed in your controller, only #relatedneeds. Is it as simple as that?
Also, your show method in the controller makes no sense to me. You have put your instance variables inside strings! You need to set your instance variables with a database query via the model first. Your get_name method looks like it belongs in a model as well rather than a controller.

Getting nested JSON output from Rails

Suppose I have a Rails app with two models Post and Comment. A post has_many comments and a comment belongs_to a post.
How can I override the respond_to function in the show action in order to get a JSON response containing both the Post properties and an array of Comment objects that it has?
Currently it is the vanilla Rails default:
# posts_controller.rb
def show
#post = current_user.posts.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
You can do that using Active Record serialization method.
to_json
Below code should work.
format.json { render json: #post.to_json(:include => :comments) }
Try using active_model_serializers for json serialization. It is easy to include associated objects and also separates things by having a different file for serialization.
Example:
class PostSerializer < ApplicationSerializer
attributes :id, :title, :body
has_many :comments
end
You can override to_json in model or you can use Jbuilder or rabl.
Rails has provide the best way to respond :
Define respond_to on the top of your controller . like :
class YourController < ApplicationController
respond_to :xml, :json
def show
#post = current_user.posts.find(params[:id])
respond_with (#post)
end
end
For more info take a look on : http://davidwparker.com/2010/03/09/api-in-rails-respond-to-and-respond-with/

Rails: include related object in JSON output

I have a note class that belongs to a user (ie a user can create many notes).
clip from my notes controller
class NotesController < ApplicationController
before_filter :authenticate_user!
respond_to :html, :xml, :json
# GET /notes
# GET /notes.xml
def index
#notes = Note.includes(:user).order("created_at DESC")
respond_with #notes
end
When I ask for the index in json results for example /notes.json, it returns the notes but only returns user_id for the user object. I would like it to also include user.username (and would be curious how to have the whole user object embedded).
Bonus question: I could not find a way to make the column show as author_id and have it relate back to user. If this is easy to do, how do you do it?
I'm not sure the new respond_to/respond_with style is flexible enough to do this. It very well may be, but as far as I understand, it's meant to simplify only the simplest cases.
You can achieve what you are trying to do with the old-style respond_to with a block, however, by passing parameters to to_json. Try something like this:
class NotesController < ApplicationController
def index
#notes = Note.order("created_at desc")
respond_to do |format|
format.json do
render :json => #notes.to_json(:include => { :user => { :only => :username } })
end
end
end
end
You can also use Jbuilder(https://github.com/rails/jbuilder) to response with data very flexible.
#notes = Note.includes(:user).order("created_at DESC")
and in your index.json.jbuilder file, you can
json.extract! #note
json.username #note.user.username
Would it be possible to do it the other way around?
def index
#user = User.includes(:notes).order("created_at DESC")
respond_with #user
end
It would be expensive to include user objects each time the #notes is iterated.

Resources