Rails active_model_serializer conditional include - ruby-on-rails

When I have a has_many/belongs_to relationship in Rails 5 API with active_model_serializers I can pass the option to include a nested model.
def show
render json: #post, include: ['comments']
end
It's also possible to get multiple layers of nesting.
def show
render json: #post, include: ['comments', 'comments.comment_likes']
end
I can't find documentation anywhere about adding conditions to the include statement. Is it possible to do something like this?
def show
render json: #post, include: ['comments'] { top_contributor: true }
end

In master (which will soon become RC4), a PR has been merged that allows for the following at serializer level:
belongs_to :user, if: :include_user?
def include_user?
current_user.admin?
end

Related

rails - Auto create tasks after creating a project (after_create)

a newbie here. Just started to learn development. Any help would be greatly appreciated
I have two models Project and Task. Each project will have 7 tasks. I want rails to auto create my 7 tasks after I create a project.
My Task Controller
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def task_params
params.require(:task).permit(:title, :description)
end
There are several ways you can do this.
1. Via callbacks
You can use callbacks in the Project model. Personally I do not recommend this approach since it this is not the intended use of callbacks, but it may work for you.
class Project < class Attachment < ActiveRecord::Base
after_create :create_tasks
private
def create_tasks
# Logic here to create the tasks. For example:
# tasks.create!(title: "Some task")
end
end
2. Nested attributes
You can build the child objects into the form and Rails will automatically create the child objects for you. Check out accepts_nested_attributes_for. This is more involved than using callbacks.
3. Use a form object
A form object can be a nice middle ground between callbacks and accepts_nested_attributes_for, but it raises the complexity a notch. Read up more about form objects here. There is also a nice Rails Casts episode on the topic, but it requires subscription.
There are other ways to do this as well, so it's up to you to find the right approach.
Another option would be to use Observer. This is more like callback.
But this is a great way to reduce the clutter that normally comes when the model class is burdened with functionality that doesn't pertain to the core responsibility of the class
class ProjectObserver < ActiveRecord::Observer
def after_create(project)
#add the logic to create tasks
end
end

active model serializer multiple collection in one template

I have this custom action which I want to save http trips to retrieve different collections with.
def dashboard
#projects = Project.all
#tasks = Task.all
respond_do do |format|
format.json {render {projects: #project, tasks: #tasks}, serializer: DashboardSerializer }
end
end
class DashboardSerializer < ActiveModel::Serializer
attributes :proejcts, :tasks
end
this gives me an error like this
undefined method `read_attribute_for_serialization' for #<Hash:0x007fb5d58108c0>
Is there any way that I can make arbitrary collection attributes in the active model serializer template as I can do in Rabl?
Thank you!
AMS has a distinction between single item serialization and item collection serialization.
I was getting the same error, my solution looked like this:
render json: #posts, each_serializer: FancyPostSerializer

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

Cannot access attr_accessor defined variables

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]) }

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/

Resources