fabrik42 / acts_as_api Goto Github PK
View Code? Open in Web Editor NEWmakes creating API responses in Rails easy and fun
Home Page: https://www.christianbaeuerlein.com/acts_as_api/
License: MIT License
makes creating API responses in Rails easy and fun
Home Page: https://www.christianbaeuerlein.com/acts_as_api/
License: MIT License
I have something very similar to the first demo example set up and I get that error in my log files and no json output.
For more details: http://stackoverflow.com/questions/11615084/rails-acts-as-api-completed-406-not-acceptable
An example, if an "Event" object has a"subject" property that is another model:
"events": [
{
"message": "...",
"subject_type": "photo",
"photo": {
"id": 1,
"thumb_url": "...",
"medium_url": "...",
"large_url": "..."
}
},
{
"message": "...",
"subject_type": "user",
"user": {
"id": 1,
"username": "...",
}
}
]
}
The "photo" and "user" element key is not known until runtime.
Hello
I already searched trough the wiki but i didn't find a solution.
I have two arrays / ActiveRecord relations which contain objects from the same model which has
api_accessible :name do |t|
t.add :id
t.add :first_name
t.add :last_name
end
The arrays represent two groups of different users
and i want to render them in one JSON object which contains both of the arrays like
-{
Can i realize this using acts_as_api?
Thanks for your help!
I use Mongoid and do not have AR loaded
format.json { render_for_api :v1_public, :json => [@user] }
works fine
however,
format.json { render_for_api :v1_public, :json => @user }
(without @user in an array) gives an error
uninitialized constant ActiveRecord::Relation
Hello,
I followed the instruction of the wiki in order to add MessagePack support.
I added the corresponding entry in config/initializers/mime_types.rb
Mime::Type.register "application/messagepack", :mpac
Then in controller I have this (Rails 3.2, git master, relevant bits):
class InternalsController < ApplicationController
respond_to :json, :xml, :mpac
self.responder = ActsAsApi::Responder
def user_info
respond_with(@owner, api_template: :v2_private)
end
The problem is that Rails is trying to render a messagepack view.
# Tell acts_as_api that it should accept an additional API format.
ActsAsApi::Config.accepted_api_formats << :mpac
# Add MessagePack as a Rails renderer.
ActionController::Renderers.add :mpac do |object, options|
self.content_type ||= Mime::MPAC # differs from the wiki, using this can result in a potential error due to loading order, let's go on for now though
self.response_body = MessagePack.pack(object)
end
# MessagePack doesn't come with the ability to serialize a TimeWithZone, monkey patch.
module ActiveSupport
class TimeWithZone
def to_msgpack(out = '')
self.to_s.to_msgpack(out)
end
end
end
RSpec tests fails:
Failure/Error: get 'user_info', format: 'mpac', k: string
ActionView::MissingTemplate:
Missing template internals/user_info, application/user_info with {:locale=>[:en, :en], :formats=>[:mpac], :handlers=>[:erb, :builder, :coffee, :haml]}. Searched in:
* "#<RSpec::Rails::ViewRendering::EmptyTemplatePathSetDecorator:0x007fcbe04a7f28>"
Am I missing something? Thanks!
Update: browsing the Rails source code in git I noticed that the ActionController::Renderers.add method never sets respond_body within the block, but just returns a string. So I tried this in the initializer:
ActionController::Renderers.add :mpac do |mpac, options|
self.content_type ||= Mime::MPAC
mpac.respond_to?(:to_msgpack) ? mpac.to_msgpack(options) : mpac
end
However rspec still fails with the same error. I can tell that mpac is loaded in renderers:
1.9.3p0 :001 > ActionController::Renderers::RENDERERS
=> #<Set: {:json, :js, :xml, :acts_as_api_jsonp, :mpac}>
E.g.
api_accessible :private, :extend => :public do
end
api_accessible :public do
end
This gives an unhelpful:
can't convert nil into Hash error in a different place.
Suggest this:
def api_accessible_attributes(api_template)
begin send "api_accessible_#{api_template}".to_sym rescue nil end
end
detect and print warnings if method is not defined.
If I have
class Sound < ActiveRecord::Base
class ShortSound < Sound
class LongSound < Sound
You have to repeat the acts as api setup identically in all three. Without it, you get:
acts_as_api template :v1 was not found for model ShortSound
The renderer should be smart enough to check the super class for the acts_as_api template setups.
Hello,
currently I have found no way on Rails 3+ to use underscores in the generated XML attributes instead of dashes.
For a legacy API version I need this functionality enabled so I have to turn if on/off dinamically, I saw some options, but nothing really specific to this issue. I tried also with:
respond_with(@collection, api_template: :v1_public, dasherize: false)
but no dice.
Hi again,
In my update
method, I have:
@project.update_attributes(params[:project])
respond_with @project, :api_template => template
My specs pass on statuses for valid and error cases.
But the response body is empty when update went well. Why? (I'd expect the object to be returned)
I have an API of posts that tells whether or not the post is liked by current_user (devise's helper method for Current Logged in user). How can I use this method in template? I see that you can pass options to template, but I am not able to get how can I use it in List of Posts. Please Help.
Also I am using REE 1.8.7
First, thanks so much for acts_as_api—very useful!
I'm trying to use it with Paperclip, but I'm having some trouble. I have a model with:
has_attached_file :attachment
And I'd like to be able to include :attachment in my acts_as_api template (template.add :attachment)
Attempting to do so like that gives me the following error:
SystemStackError (stack level too deep):
app/controllers/rooms_controller.rb:34:in show' app/controllers/rooms_controller.rb:32:in
show'
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (134.8ms)
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.3ms)
Rendered /Library/Ruby/Gems/1.8/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (154.1ms)
So clearly just adding :attachment to the template and hoping it'll magically work didn't pan out! Any ideas/suggestions for how I might tackle this? With paperclip, I can access different sizes of an uploaded image, for example, using something like @message.attachment(:large) (where @message is an object with has_attached_file and :large is one of the Paperclip-resized versions of the image).
Thanks!
By default, acts_as_api renders a json response such as follows:
{
"users": [
{
"last_name": "Skywalker",
"first_name": "Luke"
},
{
"last_name": "Solo",
"first_name": "Han"
},
{
"last_name": "Leia",
"first_name": "Princess"
}
]
}
Is it possible to render a backbone-js compatible format so it looks like this =>
[
{
"last_name": "Skywalker",
"first_name": "Luke"
},
{
"last_name": "Solo",
"first_name": "Han"
},
{
"last_name": "Leia",
"first_name": "Princess"
}
]
Thanks!
Hello i currently working on the performance of my Rails 3.1 app and I added a lot of caching for some of the JSON i am rendering using acts_as_api
What works for me is this:
def around_api_response(api_template)
Rails.cache.fetch("api_response_#{self.class.to_s}_#{id}_#{api_template.to_s}", :expires_in => 1.hour) do
yield
end
end
The render statement in the Controller is similar to this:
render_for_api :template, :json => Item.for_user(@user.id)
I found this code in one of the other issues and it reduces the time for computing the response to a quater of the time without caching. Which was about 100-200ms in my case (about 98% of this time is used for view rendering).
So i tried to do some more optimization and I did something like this
@cache = Rails.cache.fetch("cache_data_for_user_#{ @user.id }") do
#Item.for_user(@user.id).as_api_response(:template) --> load time about 300ms
Item.for_user(@user.id).as_api_response(:template).to_json --> json in cache - load time 3ms! :)
end
My problem is that if i don't use the the render_for_api method the root element "Items" is not included. My object mapper on the client depends on this root element.
I tried
ActiveRecord::Base.include_root_in_json = true # i also think it should be true by default
but it didn't help
Can i force the inclusion of the root element in some way?
Thanks!
Completed 500 Internal Server Error in 270ms
NameError (uninitialized constant ActsAsApi::Rendering::ActiveRecord):
app/controllers/companies_controller.rb:6:in `show'
Rendered /Users/siong1987/.rvm/gems/ruby-1.9.2-p180@box/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (4.3ms)
Rendered /Users/siong1987/.rvm/gems/ruby-1.9.2-p180@box/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.4ms)
Rendered /Users/siong1987/.rvm/gems/ruby-1.9.2-p180@box/gems/actionpack-3.1.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (9.2ms)
Not sure why this happens. I will dig more into the code when I have time. It works on version 0.3.8 tho.
I can't find anything about deep associations in the wiki.
I've this for example;
Gig.includes({song: [{artist: :urls}, :urls]})
I know how to include the first association (song), but how about the rest?
class Gig < ActiveRecord::Base
has_many :song
acts_as_api
api_accessible :my_api do |t|
t.add :song
end
end
like as_json(options={})
will as_api_response(template,option={}) be possible?
because I have some infos need to pass from controller to model .
In some cases, xml response maybe look like:
<users type="array">
<user>
<first-name>Me</first-name>
<duration-in-seconds type="integer">4</duration-in-seconds>
</user>
</users>
But I don't need the type='array' and type='integer', is there anyway to remove them? Just like this:
<users>
<user>
<first-name>Me</first-name>
<duration-in-seconds>4</duration-in-seconds>
</user>
</users>
In my User
model I put:
acts_as_api
devise...
And I got some weird error messages: [WARNING] You provided devise_for :users but there is no model User defined in your application
Then I simply did:
devise...
acts_as_api
And everything works again...
ActiveRecord::Relation is lazily loaded and for controllers that return collections the logic in
https://github.com/fabrik42/acts_as_api/blob/master/lib/acts_as_api/rendering.rb
is wrong. The following code is the pluralization logic in rendering.rb
if api_model.is_a?(Array)
api_root_name = api_root_name.pluralize
end
But at this point api_model is still an ActiveRecord::Relation object. The code should be
if api_model.is_a?(Array) or api_mode.is_a?(ActiveRecord::Relation)
api_root_name = api_root_name.pluralize
end
it would be really handy to conditionally add attributes to an api template.
class User < ActiveRecord::Base
acts_as_api
api_accessible :user_show do |t|
t.add :first_name
t.add :last_name
t.add :age
t.add :favorite_beer if age > 21
end
end
this is a contrived example, but i do have a (legacy) class that has many, many one-to-many relationships. it would be helpful to conditionally include certain attributes based on the value of another.
cheers!
Outside of my array of ActiveRecords, I want to be able to return total-results-on-server and page as metadata. acts_as_api does not allow this as anything other than an ActiveRecord or array passed in will crash.
Is this in active development? If I work on it would you take pull requests?
Hi,
Is it possible to change the @model.errors
JSON format from for example:
{"errors":{"email":["is already taken"]}}
To:
{"email":["is already taken"]}
Thanks!
It would be great to add versioning to the gem… something like
api_accessible { :v1 => {:firstname, :lastname, :age},
:v2 => {:middlename, :lastname, :sex} }
and so on
Is it possible to add some arbitrary static text to the actual node data, basically concatenating the content. for example:
template
api_accessible :payment_chart_data do |template|
template.add :month_due
template.add :count
end
The original template:
{"rows":[{"month_due":"(2010, 02)","count":"1"}}
With static text added:
{"rows":[{"month_due":"Date.UTC(2010, 02)","count":"1"}
Hi,
I'm running accross a problem using:
Controller:
respond_with @project, :api_template => :v1_public
Model:
api_accessible :v1_public do |template|
Project.accessible_attributes.to_a.each do |key|
template.add key.to_sym
end
end
I wonder why, the rendered json is prepended with projects
instead of project
. Indeed, it recognizes it's not an array:
{"projects":
{
"name": "foo"
#other attributes of the lonely project here
}
}
Just upgraded to Rails 3.1.rc1 and this was the only problem I came across
DEPRECATION WARNING: attr_accessor_with_default is deprecated. Use Ruby instead!. (called from singletonclass at /Users/bradphelan/.rvm/gems/ruby-1.9.2-p180@mysugrapp/bundler/gems/acts_as_api-86114e761f32/lib/acts_as_api/config.rb:13)
belongs_to :order, :counter_cache => :contributors_count
contributors_count is null in the response, but 1 in the model
Next request. It's 1 in the response and 2 in the model.
I've resorted to callbacks for this for now. But really like this out of the box.
Basically the attribute isn't set when the response is sent.
Hello,
First of all many thanks for your awesome gem, it could save a lot of my time if only I could find it earlier :)
So the problem is that my model fields are ordered wrong in JSON or XML output.
I have this in my model:
api_accessible :full_profile do |t|
t.add :id
t.add :username
t.add :status
t.add :text_status
t.add :status_updated_at
t.add lambda {|user| user.picture.url(:medium)}, :as => :picture
t.add :age
t.add :bio
t.add :lat
t.add :long
t.add :registered_time_ago
end
And this is what I have in XML output:
<user>
<status type="integer">1</status>
<picture>/system/pictures/1/medium/various_human_21.jpg</picture>
<age nil="true"></age>
<lat>39.7756093397402</lat>
<long>-73.1707363222342</long>
<text_status nil="true"></text_status>
<username>user1</username>
<bio nil="true"></bio>
<id type="integer">1</id>
<status_updated_at nil="true"></status_updated_at>
<registered_time_ago>1 day</registered_time_ago>
</user>
I am writing a web service for iOS app, and the iOS developer says that each time I deploy the code the fields are ordered in some random way, and it messes things up. The strict order is important for the XML parsing library that he uses.
Is there any solutions for this issue?
Thanks.
Switching from MRI 1.9.3
to 2.0
yields the following error:
NoMethodError (undefined method `empty?' for nil:NilClass):
lib/ext/acts_as_api/responder.rb:9:in `to_html'
This happens in a controller that looks like this:
class SomeController < ApplicationController
def index
respond_with SomeModel.all, :api_template => :public
# respond_with [], :api_template => :public
# respond_with nil, :api_template => :public
end
end
Whether you pass in SomeModel.all
, []
or nil
, all of these yields the same error.
https://gist.github.com/1077713
Tests fail with respond_with
Tests pass with just using render
I am using the acts_as_api responders in my ApiController (parent controllers)
Without any sort of API versioning, I have my controllers in a V1, V2 namespace such that the controllers are V1::FooController < ApplicationController
The render_for_api method is thus not added to the controller as it cannot find them due to the namespace. I suggest adding support for namespaces and/or api versioning.
Hello!
With STI, the current default behavior is such that any additions to the inherited template will reflect on the root template as well.
I don't know if this behavior is preferred or accidental, but it can cause the following error when rendering:
can't add a new key into hash during iteration
The fix is simple, shown here:
But I haven't make a pull request because I have several other commits stacked on that branch as well.
Hello,
I'm trying out acts_as_commentable (https://github.com/jackdempsey/acts_as_commentable)
So far the gem is working well, however when I try to render the association comments
it goes into a loop:
SystemStackError: stack level too deep
from /Users/kain/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/irb/workspace.rb:80
Sample code for my Item model:
acts_as_api
acts_as_commentable
api_accessible :public do |template|
template.add ->(item) { item.comments.recent.limit(3) }, as: :recent_comments
end
When rendering this template I get a lot of identical queries until it bombs out:
item.as_api_response(:public)
---
Comment Load (0.6ms) SELECT "comments".* FROM "comments" WHERE "comments"."commentable_id" = 1 AND "comments"."commentable_type" = 'Item' ORDER BY created_at ASC, created_at DESC LIMIT 3
I'm not sure where to start debugging this as everything seems working beside this.
Any hint is appreciated, thanks.
Some sample of a Text
Object
#texts_controller.rb
def index
@texts = current_user.company.texts
respond_to do |format|
format.html
format.json { render_for_api :texts_all, :json => @texts, :root => :texts }
end
end
#text.rb
acts_as_api
api_accessible :texts_all do |template|
template.add :title
template.add :content
template.add :attachments
end
my Text
Model does have a boolean atrribute called activated
.
is there a way to filter the unless activated?
texts from the api so that they get not shown in my json? i want to see them on the html page however.
any ideas?
Hello,
Apparently the utf8 characters in the gemspec is making the gem fail in some configurations of bundler. I forked it and removed the utf8 characters and it's back to normal.
The thing is, there is a flag for #encoding at the tol of the .gemspec file, maybe we should add it to the top of the Gemfile also.
Thanks for the great gem :)
Passing the :root parameter to esatblish the name of the root element array and each element is not working correctly. The fda-recall(s) below should just be "recall(s)"
format.json { render_for_api api_template, :json => @recalls, :meta => metadata, :root => Recall.model_name.pluralize }
<?xml version="1.0" encoding="UTF-8"?>
<fda-recalls>
<page>1</page>
<total type="integer">92847</total>
<count type="integer">25</count>
<fda-recalls type="array">
<fda-recall>
<long-description>
Ran up against a number of issues while trying to integrate acts_as_api into a metal controller.
top of the class looks like this so far:
class Api::V1::UsersController < ActionController::Metal
include AbstractController::Logger
include ActionController::UrlFor
include ActionController::Testing
include Rails.application.routes.url_helpers
include ActionController::Rendering
include ActionController::Renderers::All
include ActionController::MimeResponds
self.responder = ActsAsApi::Responder
... but i get problems with missing a 'default_render' method, among other things. Any idea how to get this to work with metal? I don't want all the other stuff that comes along with my app's ApplicationController and I want it to be super-fast...
Hello,
I'm trying to get around the best way to add session data to my templates.
For instance, my user1 is requesting the profile of user2 with the user http://domain/users/2.json
My template looks like something like that :
api_accessible :default do |t|
t.add :id
t.add :name
t.add :avatar_image
t.add lambda{|u| u.following?(current_user)}, :as => :following
end
But the current_user variable is not available in the model. Is there a way around that with the current implementation of the gem ?
Thanks for your help !
When using the respond_with
rendering method, any data added with the :meta
key is ignored:
respond_with(@events, :api_template => :public, :root => :results, :meta => api_collection_meta(@events))
Where the default respond_to
block works as expected:
respond_to do |format|
format.html # index.html.erb
format.json { render_for_api :public, json: @events, :root => :results, :meta => api_collection_meta(@events) }
end
How do associations get rendered when using ActsAsApi? Can I specify an API template for them, too?
Something like:
class User < ActiveRecord::Base
acts_as_api
api_accessible :include_tasks do |t|
t.add :tasks, :api_template => :include_subtasks
end
end
Ok, I just read this in your advanced examples: "Note: If the model tasks has also an api template :tasks_included, this will automatically be taken for rendering!"
Maybe it would be nice to have a way to specify another template for an association like the above example (perhaps :api_template or :template). Just a thought. On the other side it already meets my requirements.
Thanks for that nice extension.
I have a template with two templates like the following:
api_accessible :facebook_user_public do |template|
template.add :id
template.add :first_name
template.add :last_name
template.add :uid
...
template.add :hometown, :template => :location
template.add :location, :template => :location
...
end
api_accessible :facebook_user_private, :extend => :facebook_user_public do |template|
template.add :application_authorizations
end
This worked correctly until recently. However, currently, if this object is rendered with the facebook_user_private template, I get an error that Location (the class that :hometown and :location reference) does not have a template for :facebook_user_private. I must repeat the template.add for hometown and location in the facebook_user_private template to make the error go away.
Is this the desired behavior? It seems like extending should work like OO inheritance, which does not work like this.
Thanks,
Ben
Hello,
There is a bug in the way JSON is being escaped in rails for Utf8 characters and some Emoji used in iOS. So the content is UTF8 in the database but the json is not the right one.
See this bug here : zbskii/rails@331155f
So I fixed it on my app with a monkey patch, My Comment.last.text.to_json outputs the right JSON escaped string, but acts_as_api still returns the erroneous one. Do you have an idea on how to fix this ?
Thanks :)
Hi,
I first wanted to thank you for the great work here. I just discovered this gem a couple of days ago and I got amazed as it seems exactly what I needed.
Or almost... I just came to one problem: I have to render the response according to some parameters in the request. In a specific case, I have a model with a description
attribute that can be rendered in multiple languages, so I need to find a way of passing the params[:locale]
to the renderer.
I haven't found a way to do this or maybe I'm not seeing clearly. Can you please point me in the right direction?
Thanks!
The idea is that I would be able to fine tune a config block in the model to use memcache, expiration time, etc…
When the renderer renders the model instance as json, xml or whatever, the rendered element gets cached so that if it appears again on some other api call, it is there.
For simple models this is not necessary and could even slow things down, but on more complex models, where the api is calling methods that may induce joins or database calls, this can be a life saver.
I think that some automatic key should be generated such as api_template_#{template}model#{class.name}id#{instance.id} and use that to query.
What do you think?
I'm sorry, this is not so much an issue as a simple question but I didn't find any links to any forums, etc, so I'm posting here. Please inform me if there is such a place.
I just want to be able to reuse the data exposed by this acts_as_api concept. There is the "name_only" example of a template. How could I get a JSON or XML representation of my model object for the "name_only" example?
I want to do something simple like this:
object_as_json = @object.get_acts_as_api_data(:template => :name_only, :format => :json)
(I understand that one of the benefits with acts_as_api is that you don't have to explicate json vs xml (vs others) in the controller, but explicitly doing so in my situation is fine.)
My real world situation here is that I'm testing. I'm simulating data coming from an external system with a certain representation, and it'd be great if I could reuse the data exposed by the api templates.
Will there be mongoid support in this great plugin?
Hello again, Christian.
Would it be possible to also add an option for setting roots by template?
E.g. when rendering something to JSON in acts_as_api with :root => :users
then it results in something like this:
{ users: [ { name: "tina" }, { name: "eva" } ] }
it would be nice to render it easily this way (maybe by adding a :root option to :api_accessible):
{ users: [ { user: { name: "tina" } }, { user: { name: "tina" } } ] }
The last one is also the default output of render :json => users
.
I understand how the api_templates work with get and index, but I'm unclear how or if they work with creates & updates.
My goal is to create & accept json with nested attributes that are accepted on the parent without the _attributes suffix?
Is this possible/supported?
Hello,
In my rails views, whenever I need to call parent items for a collection, I use includes like so.
Photo.includes(:comments).page(3)
This way each time I iterate through my photos in the view it doesn't have to do a SELECT statement to get the comments of the photo.
Acts_as_api doesn't work with include apparently and makes SELECT call for every parent or child association.
I'll look it up.
When you want to supply the data in an json object like {response: [{object: ...},{object: ...}], status: "Ok" } its not possible to disable the pluralize call on the root param (here response) so it is always returned as responses when the api returns multiple elements
This is more of an feature request and thanks for the great gem
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.