Coder Social home page Coder Social logo

rabl-rails's Introduction

RABL for Rails Build Status

rabl-rails is a ruby templating system for rendering your objects in different format (JSON, XML, PLIST).

This gem aims for speed and little memory footprint while letting you build complex response with a very intuitive DSL.

rabl-rails targets Rails 4.2/5/6 application and have been testing with MRI and jRuby.

Installation

Install as a gem :

gem install rabl-rails

or add directly to your Gemfile

gem 'rabl-rails', '~> 0.6.0'

Overview

The gem enables you to build responses using views like you would using HTML/erb/haml. As example, assuming you have a Post model filled with blog posts, and a PostController that look like this:

class PostController < ApplicationController
  def index
	 @posts = Post.order('created_at DESC')
  end
end

You can create the following RABL-rails template to express the API output of @posts

# app/views/post/index.rabl
collection :@posts

attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(@user) }

This would output the following JSON when visiting http://localhost:3000/posts.json

[{
  "id" : 5, title: "...", subject: "...",
  "user" : { full_name : "..." },
  "read" : true
}]

How it works

This gem separates compiling, ie. transforming a RABL-rails template into a Ruby hash, and the actual rendering of the object or collection. This allows to only compile the template once (when template caching is enabled) which is the slow part, and only use hashes during rendering.

The drawback of compiling the template outside of any rendering context is that we can't access instance variables like usual. Instead, you'll mostly use symbols representing your variables and the gem will retrieve them when needed.

There are places where the gem allows for "dynamic code" -- code that is evaluated at each rendering, such as within node or condition blocks.

# We reference the @posts varibles that will be used at rendering time
collection :@posts

# Here you can use directly the instance variable because it
# will be evaluated when rendering the object
node(:read) { |post| post.read_by?(@user) }

The same rule applies for view helpers such as current_user

After the template is compiled into a hash, rabl-rails will use a renderer to create the actual output. Currently, JSON, XML and PList formats are supported.

Configuration

RablRails works out of the box, with default options and fastest engine available (oj, libxml). But depending on your needs, you might want to change that or how your output looks like. You can set global configuration in your application:

# config/initializers/rabl_rails.rb

RablRails.configure do |config|
  # These are the default
  # config.cache_templates = true
  # config.include_json_root = true
  # config.json_engine = ::Oj
  # config.xml_options = { :dasherize => true, :skip_types => false }
  # config.enable_jsonp_callbacks = false
  # config.replace_nil_values_with_empty_strings = false
  # config.replace_empty_string_values_with_nil = false
  # config.exclude_nil_values = false
  # config.non_collection_classes = Set.new(['Struct'])
end

Usage

Data declaration

To declare data to use in the template, you can use either object or collection with the symbol name or your data.

# app/views/users/show.json.rabl
object :@user

# app/views/users/index.json.rabl
collection :@users

You can specify root label for the collection using hash or :root option

collection :@posts, root: :articles
#is equivalent to
collection :@posts => :articles

# => { "articles" : [{...}, {...}] }

There are rares cases when the template doesn't map directly to any object. In these cases, you can set data to false.

object false
node(:some_count) { |_| @user.posts.count }
child(:@user) { attribute :name }

If you use gems like decent_exposure or focused_controller, you can use your variable directly without the leading @

object :object_exposed

Attributes / Methods

Adds a new field to the response object, calling the method on the object being rendered. Methods called this way should return natives types from the format you're using (such as String, integer, etc for JSON). For more complex objects, see child nodes.

attributes :id, :title, :to_s

You can aliases these attributes in your response

attributes :my_custom_method, as: :title
# => { "title" : <result of my_custom_method> }

or show attributes based on a condition. The currently rendered object is given to the proc condition.

attributes :published_at, :anchor, if: ->(post) { post.published? }

Child nodes

Changes the object being rendered for the duration of the block. Depending on if you use node or glue, the result will be added as a new field or merged respectively.

Data passed can be a method or a reference to an instance variable.

For example if you have a Post model that belongs to a User and want to add the user's name to your response.

object :@post

child(:user, as: :author) do
	attributes :name
end
# => { "post": { "author" : { "name" : "John D." } } }

If instead of having an author node in your response you wanted the name at the root level, you can use glue:

object :@post

glue(:user) do
  attributes :name, as: :author_name
end
# => { "post": { "author_name" : "John D." } }

Arbitrary data source can also be passed:

# in your controller
# @custom_data = [...]

# in the view
child(:@custom_data) do
	attributes :id, :name
end
# => { "custom_data": [...] }

You can use a Hash-like data source, as long as keys match a method or attribute of your main resource, using the fetch keyword:

# assuming you have something similar in your controller
# @users_hash = { 1 => User.new(pseudo: 'Batman') }

# in the view
object :@post

fetch(:@users_hash, as: :user, field: :user_id) do
  attributes :pseudo
end
# => { user: { pseudo: 'Batman' } }

This comes very handy when adding attributes from external queries not really bound to a relation, like statistics.

Constants

Adds a new field to the response using an immutable value.

const(:api_version, API::VERSION)
const(:locale, 'fr_FR')

Lookups

Adds a new field to the response, using rendered resource's id by default or any method to fetch a value from the given hash variable.

collection :@posts

lookup(:comments_count, :@comments_count, field: :uuid, cast: false)
# => [{ "comments_count": 3 }, { "comments_count": 6 }]

In the example above, for each post it will fetch the value from @comments_count using the post's uuid as key. When the cast value is set to true (it is false by default), the value will be casted to a boolean using !!.

Custom nodes

Adds a new field to the response with block's result as value.

object :@user
node(:full_name) { |u| u.first_name + " " + u.last_name }
# => { "user" : { "full_name" : "John Doe" } }

You can add condition on your custom nodes. If the condition evaluates to a falsey value, the node will not added to the response at all.

node(:email, if: ->(u) { u.valid_email? }) do |u|
	u.email
end

Nodes are evaluated at rendering time, so you can use any instance variables or view helpers within them

node(:url) { |post| post_url(post) }

If the result of the block is a Hash, it can be directly merge into the response using merge instead of node

object :@user
merge { |u| { name: u.first_name + " " + u.last_name } }
# => { "user" : { "name" : "John Doe" } }

Extends & Partials

Often objects have a basic representation that is shared accross different views and enriched according to it. To avoid code redundancy you can extend your template from any other RABL template.

# app/views/shared/_user.rabl
attributes :id, :name

# app/views/users/show.rabl
object :@user

extends('shared/_user')
attributes :super_secret_attribute

#=> { "id": 1, "name": "John", "super_secret_attribute": "Doe" }

When used with child node, if they are the only thing added you can instead use the partial option directly.

child(:user, partial: 'shared/_user')

# is equivalent to

child(:user) do
  extends('shared/_user')
end

Extends can be used dynamically using rendered object and lambdas.

extends ->(user) { "shared/_#{user.client_type}_infos" }

Partials can also be used inside custom nodes. When using partial this way, you MUST declare the object associated to the partial

node(:location) do |user|
	{ city: user.city, address: partial('users/address', object: m.address) }
end

When used this way, partials can take locals variables that can be accessed in the included template.

# _credit_card.rabl
node(:credit_card, if: ->(u) { locals[:display_credit_card] }) do |user|
  user.credit_card_info
end

# user.json.rabl
merge { |u| partial('_credit_card', object: u, locals: { display_credit_card: true }) }

Putting it all together

rabl-rails allows you to format your responses easily, from simple objects to hierarchy of 2 or 3 levels.

object :@thread

attribute :caption, as: :title

child(:@sorted_posts, as: :posts) do
  attributes :title, :slug

  child :comments do
		extends 'shared/_comment'
    lookup(:upvotes, :@upvotes_per_comment)
	end
end

Other features

And more in the WIKI

Performance

Benchmarks have been made using this application, with rabl 0.13.1 and rabl-rails 0.5.0

Overall, rabl-rails is 10% faster and use 10% less memory, but these numbers skyrockets to 50% when using extends with collection of objects.

You can see full tests on test application repository.

Authors and contributors

Want to add another format to Rabl-rails ? Checkout JSON renderer for reference Want to make another change ? Just fork and contribute, any help is very much appreciated. If you found a bug, you can report it via the Github issues.

Original idea

  • RABL Standart RABL gem. I used it a lot but I needed to improve my API response time, and since most of the time was spent in view rendering, I decided to implement a faster rabl gem.

Copyright

Copyright ยฉ 2012-2020 Christopher Cocchi-Perrier. See MIT-LICENSE for details.

rabl-rails's People

Contributors

abrisse avatar ccocchi avatar ghiculescu avatar lloydmeta avatar manuelmeurer avatar olleolleolle avatar petergoldstein avatar shkm avatar shmeltex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

rabl-rails's Issues

undefined method `upcase!' for :json:Symbol

I've gotten to the point that my RSpec tests all pass, but when I try to use the server in development mode, I keep getting

ActionView::Template::Error (undefined method `upcase!' for :json:Symbol):

Rails 3.2.11
RablRails 0.3.0

iso8601 dates break on 0.4.0

Hi,

After upgrading to 0.4.0, my dates are formated in json as "2014-12-10 17:04:01 UTC" instead of "2014-12-10T17:04:01.678Z" (iso8601) like it is on 0.3.4.

It seems an object of type JSON::Ext::Generator::State is being passed to to_json which makes the date be outputted in that format.

Any idea how to fix ?

Thanks

New way of format retrieving caused problems with gon gem

I use responders, gon and gon_responder gems to preload js variables, so with index action I have something like this:

class IvrsController < ApplicationController
  respond_to :html, :json
  def index
    respond_with ivrs, gon: { rabl: { as: :ivrs }}
  end
  ...
end

So I get ivrs collection rendered with gon in html format and plain json in json format.
But after 77f894b:

RENDERER_MAP = {
  'json'  => Renderers::JSON,
  'xml'   => Renderers::XML,
  'ruby'  => Renderers::Hash,
  'plist' => Renderers::PLIST
}.freeze
...
format = view.params[:format] ? view.params[:format].to_s.downcase : 'json'
RENDERER_MAP[format].render(compiled_template, view, locals)

with format html i get exception while trying nil.render(..)

What about fallback to json renderer?

format = view.params[:format] ? view.params[:format].to_s.downcase : 'json'
+ format = 'json' unless RENDERER_MAP.has_key? format
RENDERER_MAP[format].render(compiled_template, view, locals)

undefined method for nil:NilClass on Rails 5.0.1

Hello,

I am trying to switch over from rabl, but I receive this error on all templates. On rabl it works fine.

Take this template for example, it just returns some ids.

collection @properties
attributes :id

I am also using oj gem on Rails 5.0.1.

Do I need to do anything else?

Thanks

render :format=>:json throws an error

Documentation states you can call this:

Rabl.render(object, template, :view_path => 'app/views', :format => :json)

However, it does not work. If :json is replaced with a string instead, it works

Rabl.render(object, template, :view_path => 'app/views', :format => 'json')

reset_source_cache

Hi,

Firstly, thanks for creating this great gem :) I'm hoping to get more performance out of my Rails app with this gem.

In Rabl, there is a Rabl.reset_source_cache! method to reset source caches when cache_sources is set to true.

Is there something similar for RablRails?

Render pure Ruby

There isn't an engine for it, but is there currently a way to just return a Ruby hash? We often just want to get a pure Ruby hash representation of the data Rabl would produce. We opted to change to RablRails for the sake of speed, but can't due to not being able to render hashes.

I'd love to implement it myself with some guidance...

nil is not a Symbol

I ran into this issue when I went into production and tested in both small and big apps in development.

controller

def list
  @client_applications = @account.client_applications.page(params[:page]).per(params[:per_page])
   @message = "Application list"
    render 'client_applications/index', status: :ok
end

index.json.rabl

object false

node(:client_applications) { partial('client_applications/_show', object: @client_applications) }

extends 'shared/success'

I made a request a request to the list action and this was the output the first time.
Server

Processing by Api::Web::V1::ClientApplicationsController#list as JSON
  Parameters: {"account_id"=>"5"}
  Account Load (0.6ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = 5 LIMIT 1
  ClientApplication Load (0.4ms)  SELECT "client_applications".* FROM "client_applications" WHERE "client_applications"."account_id" IN (5)
   (0.6ms)  SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "client_applications" WHERE "client_applications"."account_id" = 5 LIMIT 25 OFFSET 0) subquery_for_count 
  Rendered client_applications/index.json.rabl (5.0ms)
   (0.4ms)  SELECT COUNT(*) FROM "client_applications" WHERE "client_applications"."account_id" = 5
  CACHE (0.0ms)  SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "client_applications" WHERE "client_applications"."account_id" = 5 LIMIT 25 OFFSET 0) subquery_for_count 
Completed 200 OK in 244ms (Views: 16.5ms | ActiveRecord: 12.5ms)
{
    "account_id" => "5",
        "format" => "JSON",
    "controller" => "api/web/v1/client_applications",
        "action" => "list"
}

Response

{"status":"success","message":"Application list","client_applications":[]}

I made a second request to the same action and this was the output.
Server

Started POST "/private/applications/list" for 127.0.0.1 at 2013-01-24 14:23:54 -0500
Processing by Api::Web::V1::ClientApplicationsController#list as 
  Parameters: {"account_id"=>"5"}
  Account Load (0.4ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = 5 LIMIT 1
  ClientApplication Load (0.4ms)  SELECT "client_applications".* FROM "client_applications" WHERE "client_applications"."account_id" IN (5)
  Rendered client_applications/index.json.rabl (47.4ms)
Completed 500 Internal Server Error in 62ms

ActionView::Template::Error (nil is not a symbol):
    1: object false
    2:
    3: node(:client_applications) { partial('client_applications/_show', object: @client_applications) }
  app/views/client_applications/index.json.rabl:2:in `_app_views_client_applications_index_json_rabl___1939161566045685585_70366142492940'
  app/controllers/api/web/v1/client_applications_controller.rb:151:in `list'

Any subsequent request returned the same error. I restarted the server and it allowed me to make one request with a successful response and rest became errors again.

The trace showed the error pointing to this line in rabl-rails

[PROJECT_ROOT]/vendor/cache/rabl-rails/lib/rabl-rails/library.rb, line 18

Rails 4.1

Has anyone tried running rabl-rails 0.9.3 with Rails 4.1?

Since upgrading to Rails 4.1 any specs that have RABL templates are failing with the following error:

undefined method each' for nil:NilClass

Full Stack Trace:

rack (1.5.2) lib/rack/etag.rb:58:in `digest_body'
rack (1.5.2) lib/rack/etag.rb:26:in `call'
rack (1.5.2) lib/rack/conditionalget.rb:25:in `call'
rack (1.5.2) lib/rack/head.rb:11:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/flash.rb:254:in `call'
rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/cookies.rb:560:in `call'
activerecord (4.1.0) lib/active_record/query_cache.rb:36:in `call'
activerecord (4.1.0) lib/active_record/connection_adapters/abstract/connection_pool.rb:621:in `call'
activerecord (4.1.0) lib/active_record/migration.rb:380:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
activesupport (4.1.0) lib/active_support/callbacks.rb:82:in `run_callbacks'
actionpack (4.1.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/reloader.rb:73:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
railties (4.1.0) lib/rails/rack/logger.rb:38:in `call_app'
railties (4.1.0) lib/rails/rack/logger.rb:20:in `block in call'
activesupport (4.1.0) lib/active_support/tagged_logging.rb:68:in `block in tagged'
activesupport (4.1.0) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (4.1.0) lib/active_support/tagged_logging.rb:68:in `tagged'
railties (4.1.0) lib/rails/rack/logger.rb:20:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
rack (1.5.2) lib/rack/runtime.rb:17:in `call'
activesupport (4.1.0) lib/active_support/cache/strategy/local_cache_middleware.rb:26:in `call'
rack (1.5.2) lib/rack/lock.rb:17:in `call'
actionpack (4.1.0) lib/action_dispatch/middleware/static.rb:64:in `call'
rack (1.5.2) lib/rack/sendfile.rb:112:in `call'
railties (4.1.0) lib/rails/engine.rb:514:in `call'
railties (4.1.0) lib/rails/application.rb:144:in `call'
rack (1.5.2) lib/rack/lock.rb:17:in `call'
rack (1.5.2) lib/rack/content_length.rb:14:in `call'
rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service'
/Users/ryan/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/httpserver.rb:138:in `service'
/Users/ryan/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/httpserver.rb:94:in `run'
/Users/ryan/.rvm/rubies/ruby-2.1.0/lib/ruby/2.1.0/webrick/server.rb:295:in `block in start_thread'

My hunch is that this error is due to rabl-rails not supporting Rails 4.1 yet? I could be wrong and the error might be related to something else, thought I'd see if anyone else here had encountered this error with rabl-rails and Rails 4.1.

extends @variable

How to extends a view using a variable?

In my controller I define @resource and @extended_view and would like to extend using @extended_view which equals for exemple 'v1/events/show'

object :resource

attributes :id, :name

extends @extended_view

MessagePack renderer?

Is there a way to render MessagePack in RABL rails? Is there any documentation on how to write my own renderer for message pack?
It looks like there isn't any support because I get this error:
uninitialized constant RablRails::Renderers::MPAC

Child with partial failed to render

If following syntax is used in template

child(:@notes => :data, :partial => 'notes/_single')

Child element will produce no output.

Workaround is

child({:@notes => :data}, :partial => 'notes/_single')

Results caching with object set to false

I'm trying to figure out how to use results caching with following rabl:

object false

child :employees => :data do
    extends "employees/_single"
end

node(:success) { true }
node(:total) { employees.size }

If I just add cache then run into error ActionView::Template::Error: undefined method 'cache_key' for nil:NilClass

If add cache with a block

object false

cache { |_| "#{ employees.cache_key }" }

child :employees => :data do
  extends "employees/_single"
end

node(:success) { true }
node(:total) { employees.size }

then it can't resolve employees: ActionView::Template::Error: undefined local variable or method 'employees' for RablRails::Renderers::JSON:Module

What's the right approach here?

Can't find Rabl in initializer

I'm getting:

uninitialized constant Rabl
...app/config/initializers/rabl.rb:1:in `<top (required)>'

The initializer in question starts off with

Rabl.configure do |config|

Not sure how exactly I need to reference

rabl-rails assumes any object that respond to `each` is a collection

I'd like to use rabl-rails to render an object, but since the object responds to each, RablRails::Renderers::Base tries to render it as a collection. Here's code that reproduces the problem:

# app/views/controllers/config_controller.rb
class ConfigController < ApplicationController
  respond_to :json

  def show
    struct = Struct.new(:foo)
    @config = struct.new("bar")
  end
end
# app/views/config/show.json.rabl
object :@config
attribute :foo

I would expect this to render {"foo":"bar"} since @config.foo returns "bar". However, because @config responds to each, Renderers::Base#render calls render_collection on it instead of render_resource, yielding an error:

undefined method `foo' for "bar":String
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:68:in `block in render_resource'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:64:in `each'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:64:in `inject'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:64:in `render_resource'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:116:in `block in render_collection'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:116:in `each'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:116:in `map'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:116:in `render_collection'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:32:in `block in render'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:55:in `render_with_cache'
  (gem) rabl-rails-0.3.3/lib/rabl-rails/renderers/base.rb:31:in `render'
  ...

(You can see the complete backtrace here: https://gist.github.com/jrunning/9279963)

This might seem like a contrived example, but I ran into it in a real scenario (I was trying to render attributes of an ActiveSupport::OrderedOptions rather than a struct). The same issue would manifest if someone defined each on an ActiveRecord model.

I'm not sure what the best solution is. Perhaps object could take an option that would explicitly tell it not to render the object as a collection?

For now my only option seems to be falling back to the regular rabl gem, which lacks some of the Railsy niceties of rabl-rails.

rabl-rails renders JSON instead of XML in some cases

Hi, I came across rabl-rails and wanted to use it because of the performance gains, but unfortunately, for some reason, it's not working in my Rails 4.1.8 app with Ruby 2.1.3, whereas the original rabl gem works. The only difference is the gems (and using collection @locations versus collection :@locations). When I used rabl-rails, the controller was rendering JSON instead of XML. Then when I replaced rabl-rails with rabl, everything was working fine.

I have no idea where to start looking to troubleshoot why rabl-rails doesn't work, but here is the source code:
https://github.com/smcgov/ohana-api-smc

Here is the rabl template:
https://github.com/smcgov/ohana-api-smc/blob/master/app/views/api/v1/locations/index.xml.rabl

Here is the controller:
https://github.com/smcgov/ohana-api-smc/blob/master/app/controllers/api/v1/locations_controller.rb

Note that this template now has rabl-specific stuff in it, but you can still reproduce the issue with just a basic template that only defines attributes.

Polymorphic Association

I have been using Rabl and I thought I should give rabl-rails a try as it promises faster responses.
I am using public_activity gem which has a polymorphic belongs_to
belongs_to :trackable, :polymorphic => true

When I use child :trackable while using rabl, everything works as expected, and the key of the child object is actually the model name. But in rabl-rails the key comes as it is, i-e 'trackable' and its empty.

An example
with rable

{
    "type": "Event",
    "users": [{
        "id": 898,
    }],
    "time": "2013-09-10T15:31:12Z",
    "event": {
        "id": 27,
        "name": "Hold it",
    }
}

with rabl-rails

{
    "type": "Event",
    "users": [{
        "id": 898,
    }],
    "time": "2013-09-10T15:31:12Z",
    "trackable": null
}

child() should be nil aware

Recently I was working on a template with a parent / child... let's say Post / Author.

object :@post
attribute(:name)
child :author do
extends "author/base"
end

This will render the child block even if @post.author.nil?, which will throw an error.

Shouldn't child() be aware if the relationship is nil?

Unable to access view helpers outside of node

I'm unable to access view helpers in my rabl templates outside of a node block. This would be nice to conditionally include nodes to a json representation.

#index.json.rabl
collection @user_events

if user_signed_in?
  node :hello do
    "world"
  end
end
undefined method `user_signed_in?' for #<RablRails::Compiler:0x00000104564f28>

passing locals

How do you pass locals in rabl-rails?
The syntax from the rabl gem doesn't work: extends('posts/show', :locals => { :hide_comments => true })
extends in rabl-rails seems to accept only one argument

`exclude_nil_values` not working

I'm using Rails 3.2.17 on Mac OS, with RABL-rails v0.3.4.

When I set this configuration:

RablRails.configure do |config|
  config.exclude_nil_values = true
end

I receive:
NoMethodError: undefined methodexclude_nil_values=' for RablRails:Module`

Object root not included in 0.4.2, but was in 0.4.1

When upgrading rabl-rails from 0.4.1 to 0.4.2 we noticed that in our staging environment the object root is no longer included but it was in 0.4.1. We use latest responders gem with rabl-rails and are on Rails 4.2.6. Here's a sample RABL file from our API that demonstrates the issue:

object :@user

extends 'api/v1/users/user'

node(:total_credits) do |user|
  user.get_credits[:total]
end

child(:credit_cards) do
  extends 'api/v1/credit_cards/credit_card'
end

child(:bank_account) do
  extends 'api/v1/bank_accounts/bank_account'
end

So instead of getting something like the following with 0.4.1:

{
  "user": {
    "id": 1,
    "name": "Javier Julio"
  }
}

We are now getting the following with 0.4.2 which is missing the root key

{
  "id": 1,
  "name": "Javier Julio"
}

We don't have any configuration for rabl-rails for staging or anywhere for that matter (no initializer, just using defaults) so I wonder if it has to do with how templates get compiled that is causing this bug?

Not being able to use a cache key in rabl generated by controller.

Hi,
I've been using rabl-rails for sometime now and its great. But I'm not able to use a cache key generated by controller in the rabl template.

Here is the code

def some_action
   @cache_key = "#{params[:x]}-#{params[:y]}"
end

some_action.json.rabl

object :@brand, root: false
cache {|b| @cache_key}

Also I can't seem to be able to access the params hash in the rabl template. Is this intended ? How can I achieve this ?

Thanks in advance.

Object 'nil' with extends

When using extends I'm getting the following error:

[FATAL] [2015-06-03 15:03]   ActionView::Template::Error (no implicit conversion of nil into String):
    1: collection :@contacts
    2: 
    3: extends 'contacts/show'
  app/views/contacts/index.json.rabl:3:in `_app_views_contacts_index_json_rabl__1238373977362342878_70108584054580'
  app/controllers/api/v1/contacts_controller.rb:5:in `index'

contacts/index.json.rabl:

collection :@contacts
extends 'contacts/show'

contacts/show.json.rabl

object :@contact
node(:id)        { |contact| contact.id.to_s }
attributes       :name

If I render only index, it works perfectly, but after rendering show and then rendering index again, it gives me the error. If I then change node(:id) { |contact| contact.id.to_s } to node(:id) { |c| c.id.to_s }it works again, until I render the show.
If I remove node(:id) { |contact| contact.id.to_s }it works perfectly normal
Index also works if I do object :contact(w/o the @) in show.json.rabl but then ofcourse the show method fails.

Error Testing with Rspec

I'm testing the responses with rspec. I previously had set up the views with the rabl gem but decided to try rabl-rails. All my tests were passing on using the rabl gem but once I made the switch to rabl-rails the tests started failing.

The weird thing is that they fail randomly and depending on the seed of the tests. I get the following error:

Failure/Error: post :list
     ActionView::Template::Error:
       can't convert nil into String
     # (eval):13:in `compile_source'
     # ./app/views/posts/index.json.rabl:2:in `_app_views_posts_index_json_rabl___1097657160476041507_70197771010180'
     # ./app/controllers/api/v1/posts_controller.rb:300:in `list'
     # ./spec/controllers/api/v1/posts_controller_spec.rb:218:in `block (4 levels) in <top (required)>'

Here's my view for that specific call:

object false
node(:posts) { partial('posts/_show', object: @posts) }
extends 'shared/success'

All my tests fail with that same error but the line # (eval):13:in 'compile_source' has different line numbers on different views.

Also, it is very random. Depending on the order of the tests, different views will fail but the errors are always the same. Any idea why this is happening?

Edit
Another thing to point out is that not all the views fail depending on the order. Certain views will fail sometimes but others will pass.

Only one node present and rendered, but executed twice.

I might be misunderstanding something, but it does appear that for my single node it is running the block twice. This may also be an issue with RABL as opposed to rabl-rails, just let me know if I'm in the wrong place.

Here's my complete show.json.rabl:

object :@gizmo
attributes :id, :title, :description, :template

node :data do |gg|
  Rails.logger.info "ONCE........................\n\n"
  gizmo_data_params.empty? ? {} : gg.execute(gizmo_data_params)
end

Here's the (redacted) JSON output:

{"gizmo":{"id":5,"title":"Title","description":null,"template":"infohub","data":{"infohub":{"options":{"start_date":"2014-11-14","end_date":"2014-11-20","fids":[],"cids":[]}}}}}

There's only 1 'data' node in the output, but when debugging (binding.pry) the #execute method, triggered from the RABL node, I see that it executes twice. The log statement also appears twice in the console for each request.

Should this be happening as a standard practice? Is there something about my case in particular that would cause this? Thanks for any help!

Upgrading to 0.5.0 from 0.4.3 encountering error "undefined method `render' for RablRails:Module"

Hello,

In our Rail-based API-only app we are generating JSONs by rendering object directly.

There are cases when you want to render object outside Rails view context. For instance to render objects in the console or to create message queue payloads. For these situations, you can use RablRails.render as show below:

RablRails.render(object, template, :view_path => 'app/views', :format => :json) #=> "{...}

Recently we have started upgrading our Rails to 5.1.2 and as part of bundle update rabl-rails gem got updated to v 0.5.0 from 0.4.3 (bundle update output snippet)

Fetching rabl-rails 0.5.0 (was 0.4.3)
Installing rabl-rails 0.5.0 (was 0.4.3)

Now we are running our rspec and encountering following error:

     Failure/Error: RablRails.render(serializable_object, template.name, view_path: template.root_path_for_api_version.to_s, format: :json)
     
     NoMethodError:
       undefined method `render' for RablRails:Module

I compared the source-code and found that in v 0.4.3 RablRails::Renderer module exists, however in 0.5.0 tree it is removed

Found this commit 12a3f0f in which RablRails::Renderer module was removed.

Since that is removed what is the alternate approach to render the object directly? We are using this same approach of rendering the object directly across our API-only application. So it is very critical that we need this behaviour to be available.

Note: Ours API application is built on Rails but not using rails built-in api-only features.

Please suggest the possible fixes for this removed behaviour.

ActionView::Template::Error: stack level too deep

When migrating from rabl to rabl-rails I encounter a problem. Let's say you have 2 views in which you can extends each other under 2 different conditions that cannot be true at the same time.

Postulate : condition_A = !condition_B

# x/show.rabl
object :@x
attributes :id

condition(->(_) { condition_A } do
  extends 'y/show'
end
# y/show.rabl
object :@y
attributes :id

condition(->(_) { condition_B } do
  extends 'x/show'
end

That case triggers a ActionView::Template::Error: stack level too deep error when rendered. I guess that because the views are compiled contrary to the rabl gems.

RablRails.configure throws undefined method `xml_options=' for RablRails:Module (NoMethodError)

rabl-rails version 0.3.3
rails version 3.0.9

# my config/initializers/rabl_rails.rb
RablRails.configure do |config|
  config.xml_options = { :dasherize => false, :skip_types => true }
  config.default_responder_template = 'show'
end

stacktrace:

config/initializers/rabl_rails.rb:2:in `block in <top (required)>': undefined method `xml_options=' for RablRails:Module (NoMethodError)
    from /usr/lib/ruby/gems/1.9.1/gems/rabl-rails-0.3.3/lib/rabl-rails.rb:46:in `configure'
    from config/initializers/rabl_rails.rb:1:in `<top (required)>'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:235:in `load'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:235:in `block in load'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:225:in `block in load_dependency'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:596:in `new_constants_in'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:225:in `load_dependency'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:235:in `load'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/engine.rb:201:in `block (2 levels) in <class:Engine>'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/engine.rb:200:in `each'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/engine.rb:200:in `block in <class:Engine>'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/initializable.rb:25:in `instance_exec'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/initializable.rb:25:in `run'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/initializable.rb:50:in `block in run_initializers'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/initializable.rb:49:in `each'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/initializable.rb:49:in `run_initializers'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/application.rb:134:in `initialize!'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/application.rb:77:in `method_missing'
    from config/environment.rb:5:in `<top (required)>'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:239:in `require'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:239:in `block in require'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:225:in `block in load_dependency'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:596:in `new_constants_in'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:225:in `load_dependency'
    from /usr/lib/ruby/gems/1.9.1/gems/activesupport-3.0.9/lib/active_support/dependencies.rb:239:in `require'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/application.rb:103:in `require_environment!'
    from /usr/lib/ruby/gems/1.9.1/gems/railties-3.0.9/lib/rails/commands.rb:22:in `<top (required)>'
    from script/rails:7:in `require'
    from script/rails:7:in `<main>'

Rspec - Failure/Error: render - ActionView::Template::Error

I switched from rabl to rabl-rails. It works great except during the tests (rspec).

My view:

object :@media

attributes :types

My test:

describe 'v1/medias/show' do
  it 'should render a valid json' do
    render # it fails here
    expect { JSON.parse(rendered) }.to_not raise_error(JSON::ParserError)
  end
end

The error:

 Failure/Error: render
     ActionView::Template::Error:
       undefined method `start_with?' for :__memoized:Symbol
     # ./app/views/v1/medias/show.rabl:2:in `_app_views_v__medias_show_rabl___3750579331352501291_55842580'
     # ./spec/views/v1/medias_view_show_spec.rb:14:in `block (2 levels) in <top (required)>'

Problem with request.format

request.format isn't always set with rabl-rails. I had no problem with rabl.

The response header 'Content-Type' is sometimes invalid too. (return text/html instead of application/json during the first request in my app).

Any idea ?

You can't render anonymous child nodes

The original rabl gem allows the option

config.include_child_root = false

This is not possible with rabl-rails.
Also you can render an anonymous node in the original rabl with

node do |obj|
      { some_att: obj.some_val }
end

This is also not possible with rabl-rails as node have a mandatory node-name argument
If you supply nil as an argument to node, the node root will just be an empty string.

Is there any way to work around this?

Other renderer support (plist?)

Any plans on adding more renderers, like plist? I'm using rabl now, was considering switching over for better performance, but i'm using the plist renderer for a couple endpoints. Is this something easy to add?

undefined method 'collection'

Sometimes - not always:

Started GET "/api/collections" for 95.25.169.19 at 2012-10-22 13:50:53 +0000
Processing by Api::CollectionsController#index as HTML
Rendered api/collections/index.rabl (3.0ms)
Failure: undefined method collection' for #<Collection:0x00000004f57d38> Class: NoMethodError /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/activemodel-3.2.8/lib/active_model/attribute_methods.rb:407:inmethod_missing'
/var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/activerecord-3.2.8/lib/active_record/attribute_methods.rb:149:in method_missing' /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:56:inblock in render_resource'
/var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:51:in each' /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:51:ininject'
/var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:51:in render_resource' /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:97:inblock in render_collection'
/var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:97:in map' /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:97:inrender_collection'
/var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/renderers/base.rb:30:in render' /var/www-data/mydreamboardapp.com/shared/bundle/ruby/1.9.1/gems/rabl-rails-0.2.2/lib/rabl-rails/library.rb:18:inget_rendered_template'
/media/data1/www-data/mydreamboardapp.com/releases/20121022130156/app/views/api/collections/index.rabl:2:in `_app_views_api_collections_index_rabl__728353672390700706_51375400'

Mos of the time ir works fine.

index.rabl:

collection :@collections

extends 'api/commons/collection'

Is cache for templates using collection avail?

The following

app/views/users/index.json.rabl

collection :@users
cache
extends "users/show"

will raise an error as such:

undefined method `cache_key' for #User::ActiveRecord_AssociationRelation:0x007fdee2c55350

Been trying a few variations with extends and partials, to no avail. Thanks for any suggestions!

Response contains ":" in rails 5

hi guys,

i was just wondering if anyone else has stumbled upon this issue?

node(:data) do |user|
    {id: user.id, name: user.name}
end

returns

{":data":{":id":1,":name":"John Doe"}

any idea why something like this would occur? i am sure i am missing something stupid..
thanks!

use_custom_responder = true doesn't work on RoR 4.2

As I understood use_custom_responder make use of RablRails::Responder instead of ActionController::Responder

Doesn't seem to work (at least on RoR 4.2), you may want to add an active support hook into you railie.rb file ...

ActiveSupport.on_load :action_controller do
  self.responder = RablRails::Responder if RablRails.configuration.use_custom_responder
end

Edit : just realized responders have been extracted out from rails 4.2, the responders gem on its side has changed the prototype of the method api_behavior which doesn't take any parameter anymore

Support of block in RablRails::Compiler#child

RablRails::Compiler#child doesn't seem to support blocks.

With the gem rabl, I currently have the following view:

object :@set

attributes :id, :name, :items

child(:items => :items) do |items|
    extends get_configuration_item_view(items)
end

get_configuration_item_view is a helper that returns the view able to render the items depending of the class of these items (dynamic because of polymorphism).

How to achieve that using rabl-rails ?

Any plans of merging to the original rabl gem?

Is rabl-rails still 20% faster than rabl? If so what is the secret sauce ๐Ÿ‘ Can we merge the same into rabl gem?

It feels that everyone should have only one gem to goto and contribute.

Thanks!

Rails 4.2

If you can't get this to work in Rails 4.2. Adding require: false to your Gemfile resolves this issue. Your object appears nil the template otherwise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.