WELCOME TO RUBY
Let me address this first.
Many of you have said things about our logo.
And I don't mean nice things. 🙊
Okay, so it does look a bit like shit. 💩
A bit. Okay?
A lot. Okay!
But what is the Banitsa anyway?
Isn't it what it eventually turn into?
So, we have a new logo now.
I guess...
YOU ARE WELCOME!
Anyway...
September 22-23
Open Call for Speakers! 📈
You can help us by spreading the word!
Follow us on twitter.
Make your friends go.
Make your non-friends go.
Make some noise online.
Make some noise inline.
That (inline) doesn't make no sense!
What does make sense is the noise though.
Every single bit of it will help.
Since 5.0 we have an API friendly mode.
This doesn't mean that Rails pre 5.0 sucks for APIs.
It does not.
It's fine.
The API mode does a few things.
Cuts the internal middleware stack.
Introduces a bit slimmer ActionController::Base.
Introduces ActionController::API.
Introduces API friendly development errors.
All of this can be backported pre Rails 5.0
5.0 acknowledges a default API stack.
What makes a good API?
What makes a good RESTful API?
Good resources.
Semantic responses.
Consistent responses.
Consistent error responses.
We have a couple of options.
Renders Action View templates (views) as JSON instead of HTML.
# app/views/messages/show.json.jbuilder
json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)
json.author do
json.name @message.creator.name.familiar
json.email_address @message.creator.email_address_with_name
json.url url_for(@message.creator, format: :json)
end
if current_user.admin?
json.visitors calculate_visitors(@message)
end
json.comments @message.comments, :content, :created_at
json.attachments @message.attachments do |attachment|
json.filename attachment.filename
json.url url_for(attachment)
end
{
"content": "<p>This is <i>serious</i> monkey business</p>",
"created_at": "2011-10-29T20:45:28-05:00",
"updated_at": "2011-10-29T20:45:28-05:00",
"author": {
"name": "David H.",
"email_address": "'David Heinemeier Hansson' ",
"url": "http://example.com/users/1-david.json"
},
"visitors": 15,
"comments": [
{ "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
{ "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
],
"attachments": [
{ "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
{ "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
]
}
This is the default API mode option.
Too much DSL for my taste.
Even if you like the DSL, the logic feel like it's in one big bag.
Can be abstracted with partial views, but I like methods.
Renders a canonical representation of an object as JSON.
No templates, simple Ruby classes are used instead.
class SomeSerializer < ActiveModel::Serializer
attribute :title, key: :name
attributes :body
def body
object.strip
end
end
{
"name": "Some",
"body": "I smell something 🤔."
}
I like the concept of the serializers.
Separates the view logic.
Testable without specialized tools.
Simple
It's kinda huge.
Brings runtime dependencies.
Supports jsonapi!
But what if you don't need jsonapi?
Requires an interface for the serializable objects.
class SomeResource < ActiveRecord::Base
# columns: title, body
end
class SomeResource < ActiveModelSerializers::Model
attributes :title, :body
end
A lightweight alternative to AMS.
No interface restrictions for the serializable objects.
A minimal interface on the library side.
# app/serializers/user_serializer.rb
class UserSerializer < Serializr
attributes :id, :name, :email
def name
object.first_name + " " + object.last_name
end
end
{
"id": 42,
"name": "Doe John",
"email": "john@nsa.gov"
}
Integration with ActionController
class UsersController < ApplicationController
def show
user = User.find(params[:id])
render json: user
end
end
class UsersController < ApplicationController
def show
user = User.find(params[:id])
render json: user, serializer: UserSerializer
end
end
class FriendsController < ApplicationController
def index
friends = User.friends_of(params[:id])
render json: friends
end
end
class FriendsController < ApplicationController
def index
friends = User.friends_of(params[:id])
render json: friends, serializer: UserSerializer[]
end
end
A minimal interface on the library side.
This means no has_one, has_many, etc. class macros.
Quite easy to support.
# app/serializers/application_serializer.rb
class ApplicationSerializer < Serializr
inlcude Rails.application.routes.url_helpers
cattr_reader :serializer_class_cache do
Hash.new do |hash, cls|
hash[cls] = (cls.name + Serializer).constantize
end
end
def render_one(object, serializer: nil)
return unless object
serializer ||= serializer_class_cache[object.class]
serializer.new(object)
end
def render_many(objects, serializer: nil)
return [] if objects.blank?
serializer ||= serializer_class_cache[objects.first.class][]
serializer.new(objects)
end
end
Choose a format and stick with it!
Being an API, you have one great constraint.
You're not the one to worry about presenting the errors.
Only providing them.
Which means, we can treat all the errors the same.
Let your application crash.
Catch it and render the response.
Enjoy your substance of choice, moderately!
ActionController::API.rescue_from
ActionController::Base.rescue_from
Project you may find useful for your API.