Code Monkey home page Code Monkey logo

boxer's Introduction

Boxer

Boxer is a template engine for creating nested and multi-view JSON objects from Ruby hashes.

Logo

The Problem

Say you have a couple ActiveRecord models in your Rails app and you want to render an API response in JSON, but the view of each of those model objects may change based on the API action that's being requested.

  • User
  • Place

For instance, the API for GET /users/:id should render a full representation of the User object in question, including all relevant attributes.

But in your GET /places/:id/users API call, you only need short-form representations of the users at that place, without every single attribute being included in the response.

The Solution

Boxer allows you to define a box for each type of object you'd like to display (or for each amalgamation of objects you want to display—it's up to you).

Boxer.box(:user) do |box, user|
  {
    :name => user.name,
    :age  => user.age,
  }
end

To display different views on the same object, you can use Boxer's views:

Boxer.box(:user) do |box, user|
  box.view(:base) do
    {
      :name => user.name,
      :age  => user.age,
    }
  end

  box.view(:full, :extends => :base) do
    {
      :email      => user.email,
      :is_private => user.private?,
    }
  end
end

As you might guess, the :full view includes all attributes in the :base view by virtue of the :extends option.

Now, in order to render a User with the :base view, simple call Boxer.ship:

Boxer.ship(:user, User.first)

Boxer assumes that you want the :base view if no view is specified to ship—it's the only specially-named view.

To render the full view for the same user:

Boxer.ship(:user, User.first, :view => :full)

Which will give you back a Ruby hash on which you can call #to_json, to render your JSON response1:

>> Boxer.ship(:user, User.first, :view => :full).to_json
=> "{"name": "Bo Jim", "age": 17, "email": "[email protected]", "is_private": false}"

Composing different boxes together is as simple as calling Boxer.ship from within a box—it's just Ruby:

Boxer.box(:place) do |box, place|
  {
    :name     => place.name,
    :address  => place.address,
    :top_user => Boxer.ship(:user, place.users.order(:visits).first),
  }
end
  1. Hash#to_json requires a json library

More Features

See the wiki for more features of Boxer, including:

Installation

Install the boxer gem.

Original Author

Inspiration

Boxer was inspired by RABL, by Nathan Esquenazi.

boxer's People

Contributors

h3h avatar

Stargazers

Angus H. avatar Steve avatar Ivan Nemytchenko avatar Michel Graciano avatar Tomohiro Taira avatar Hiroshi SHIBATA avatar Stéphane Adam Garnier avatar Dennis Vaughn avatar Maurizio Del Corno avatar Jordan avatar Hassan avatar Jens Hausherr avatar Pham Quy avatar  avatar  avatar  avatar David Silva avatar Seye Kuyinu avatar ebugsky avatar Michael Bearne avatar Zhiping Yang avatar Ivan Kotelnikov avatar Sebastian Schumann avatar Martin Samami ッ avatar Instant Flashback avatar Mariusz Ołownia avatar Christian Hochfilzer avatar Rebecca Miller-Webster avatar Steven! Ragnarök avatar Oleg Kovalenko avatar  avatar jesse avatar Chris Radcliff avatar Michael May avatar Dmitry Nehaychik avatar John E. Vincent avatar Jeff Carroll avatar  avatar Felix Holmgren avatar Jose Cortinas avatar Chandra Patni avatar Héctor Ramos avatar Anders Haig avatar Sergey Enin avatar Chris Branson avatar Steve Hansell avatar Erik van der Wal avatar Brendon Murphy avatar Kevin Elliott avatar Dan Nguyen avatar Kamil Skrzypiński avatar Tors avatar Luke Chamberlain avatar Scott Ames-Messinger avatar Zellux Wang avatar Gabriel de Oliveira Barbosa avatar Jeff Heuer avatar Drew Yeaton avatar Andrew Hite avatar Cameron Priest avatar Mason Hale avatar Josh King avatar Ryan Kee avatar Craig Wickesser avatar Mike Harris avatar Riley Lynch avatar Richard Schneeman avatar Usman Bashir avatar Richard Metzler avatar Mike Javorski avatar Baris Gumustas avatar Mitsutaka Mimura avatar Xi avatar Lucas Zhang avatar Justin Powell avatar Les Fletcher avatar  avatar Namit Chadha avatar Chris Mytton avatar Johan Lundström avatar Michael Grassotti avatar Rouss Valiulin avatar Travis Jeffery avatar Stig Kleppe-Jørgensen avatar Saulius Grigaliunas avatar Howard R. avatar Nelson Yee avatar James Pozdena avatar Pierre avatar Vannarith Ek avatar Rajesh Rajappan avatar  avatar Mattt avatar Tyler Silcox avatar Maksim Pechnikov avatar Ryan Garver avatar Andrey D. Viana avatar Richard Hubers avatar Philip (flip) Kromer avatar Anderson Peligrini avatar

Watchers

Tobias Preuss avatar James Cloos avatar

boxer's Issues

Testing the Boxer API

In the Using Boxer with Rails wiki page where it says:

# Load all of our boxes
unless Rails.env.test?
  Dir[File.join(Rails.root, 'lib', 'boxer', '**', '*.rb')].each do |f|
    require_dependency f
  end
end

Why wouldn't you want to load the Boxer in the test environment?

We want to make sure these endpoints are working but we are assuming there might be a logical reason why this was suggested. Can anyone clear this up?

cc. @bcoelh0

Can I use boxer with kaminari?

Hi, this is more a question than a issue, can I paginate the results with boxer and kaminari (or will_paginate) ?

Thank you

Support for Arrays?

Can I do this or do i need to iterate over my collection to call boxer.ship

Boxer.ship(:course, Course.all)
NoMethodError: undefined method `title' for #<Array:0x007f8efb555528>
    from (irb):7:in `block (2 levels) in irb_binding'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:78:in `call'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:78:in `block in ship'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:77:in `each'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:77:in `inject'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:77:in `ship'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/boxer-1.0.2/lib/boxer.rb:45:in `ship'
    from (irb):40
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.9/lib/rails/commands/console.rb:44:in `start'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.9/lib/rails/commands/console.rb:8:in `start'
    from /Users/schneems/.rvm/gems/ruby-1.9.2-p290/gems/railties-3.0.9/lib/rails/commands.rb:23:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

Boxer.ship(:course, Course.first)
 => {:name=>"Example Name", :price=>7.11} 

If its not supported => feature suggestion. If it is supported, any clue what i'm doing wrong?

Add Ability to Output XML

By overriding #to_xml on the result class, possibly by having shipped boxes be a custom Boxer::Hash class that inherits from Hash.

NameError uninitialized constant

I run into the problem that I cannot use Boxer in my project. Here is a controller I prepared:

# /app/controllers/api/v1/depots_controller.rb
class Api::V1::DepotsController < Api::V1::BaseController
  def index
    render :json => @depots.map { |depot| Boxer.ship(:depot, depot) }
  end
  def show
    render :json => Boxer.ship(:depot, @depot, :view => :full)
  end
end

This the associated template:

# /lib/boxer/depot.rb
Boxer.box(:depot) do |box, depot|
  box.view(:base) do
    {
      :place_id => depot.place_id,
      :name => depot.name,
      :location => depot.location,
      :subtype => depot.subtype,
      :user_id => depot.user_id,
      :created_at => depot.created_at,
      :updated_at => depot.updated_at
    }
  end

  box.view(:full, :extends => :base) do
    {
      :latitude => depot.latitude,
      :longitude => depot.longitude
    }
  end
end

When I call the URL http://localhost:3000/api/v1/depots.json I end up with this error.

Started GET "/api/v1/depots.json" for 127.0.0.1 at 2013-02-03 13:08:22 +0100
Processing by Api::V1::DepotsController#index as JSON
Completed 500 Internal Server Error in 8ms

NameError (uninitialized constant Depot):
activesupport (3.2.11) lib/active_support/inflector/methods.rb:230:in `block in constantize'
activesupport (3.2.11) lib/active_support/inflector/methods.rb:229:in `each'
activesupport (3.2.11) lib/active_support/inflector/methods.rb:229:in `constantize'
activesupport (3.2.11) lib/active_support/core_ext/string/inflections.rb:54:in `constantize'
cancan (1.6.8) lib/cancan/controller_resource.rb:145:in `resource_class'
cancan (1.6.8) lib/cancan/controller_resource.rb:152:in `resource_class_with_parent'
cancan (1.6.8) lib/cancan/controller_resource.rb:41:in `authorize_resource'
cancan (1.6.8) lib/cancan/controller_resource.rb:26:in `load_and_authorize_resource'
cancan (1.6.8) lib/cancan/controller_resource.rb:10:in `block in add_before_filter'
activesupport (3.2.11) lib/active_support/callbacks.rb:407:in `_run__3725606286168836745__process_action__2615830379731452299__callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.11) lib/active_support/callbacks.rb:385:in `_run_process_action_callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.11) lib/abstract_controller/callbacks.rb:17:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/rescue.rb:29:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
activesupport (3.2.11) lib/active_support/notifications.rb:123:in `block in instrument'
activesupport (3.2.11) lib/active_support/notifications/instrumenter.rb:20:in `instrument'
activesupport (3.2.11) lib/active_support/notifications.rb:123:in `instrument'
actionpack (3.2.11) lib/action_controller/metal/instrumentation.rb:29:in `process_action'
actionpack (3.2.11) lib/action_controller/metal/params_wrapper.rb:207:in `process_action'
activerecord (3.2.11) lib/active_record/railties/controller_runtime.rb:18:in `process_action'
actionpack (3.2.11) lib/abstract_controller/base.rb:121:in `process'
actionpack (3.2.11) lib/abstract_controller/rendering.rb:45:in `process'
actionpack (3.2.11) lib/action_controller/metal.rb:203:in `dispatch'
actionpack (3.2.11) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
actionpack (3.2.11) lib/action_controller/metal.rb:246:in `block in action'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:73:in `call'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:73:in `dispatch'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:36:in `call'
journey (1.0.4) lib/journey/router.rb:68:in `block in call'
journey (1.0.4) lib/journey/router.rb:56:in `each'
journey (1.0.4) lib/journey/router.rb:56:in `call'
actionpack (3.2.11) lib/action_dispatch/routing/route_set.rb:601:in `call'
sass (3.2.5) lib/sass/plugin/rack.rb:54:in `call'
warden (1.2.1) lib/warden/manager.rb:35:in `block in call'
warden (1.2.1) lib/warden/manager.rb:34:in `catch'
warden (1.2.1) lib/warden/manager.rb:34:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.4) lib/rack/etag.rb:23:in `call'
rack (1.4.4) lib/rack/conditionalget.rb:25:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/head.rb:14:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/params_parser.rb:21:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/flash.rb:242:in `call'
rack (1.4.4) lib/rack/session/abstract/id.rb:210:in `context'
rack (1.4.4) lib/rack/session/abstract/id.rb:205:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/cookies.rb:341:in `call'
activerecord (3.2.11) lib/active_record/query_cache.rb:64:in `call'
activerecord (3.2.11) lib/active_record/connection_adapters/abstract/connection_pool.rb:479:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `_run__2844790409070389976__call__4041279565480068769__callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:405:in `__run_callback'
activesupport (3.2.11) lib/active_support/callbacks.rb:385:in `_run_call_callbacks'
activesupport (3.2.11) lib/active_support/callbacks.rb:81:in `run_callbacks'
actionpack (3.2.11) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/reloader.rb:65:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/remote_ip.rb:31:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
railties (3.2.11) lib/rails/rack/logger.rb:32:in `call_app'
railties (3.2.11) lib/rails/rack/logger.rb:16:in `block in call'
activesupport (3.2.11) lib/active_support/tagged_logging.rb:22:in `tagged'
railties (3.2.11) lib/rails/rack/logger.rb:16:in `call'
quiet_assets (1.0.1) lib/quiet_assets.rb:20:in `call_with_quiet_assets'
actionpack (3.2.11) lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.4) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.4) lib/rack/runtime.rb:17:in `call'
activesupport (3.2.11) lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.4) lib/rack/lock.rb:15:in `call'
rack-livereload (0.3.11) lib/rack/livereload.rb:55:in `_call'
rack-livereload (0.3.11) lib/rack/livereload.rb:46:in `call'
actionpack (3.2.11) lib/action_dispatch/middleware/static.rb:62:in `call'
railties (3.2.11) lib/rails/engine.rb:479:in `call'
railties (3.2.11) lib/rails/application.rb:223:in `call'
rack (1.4.4) lib/rack/content_length.rb:14:in `call'
railties (3.2.11) lib/rails/rack/log_tailer.rb:17:in `call'
thin (1.5.0) lib/thin/connection.rb:81:in `block in pre_process'
thin (1.5.0) lib/thin/connection.rb:79:in `catch'
thin (1.5.0) lib/thin/connection.rb:79:in `pre_process'
thin (1.5.0) lib/thin/connection.rb:54:in `process'
thin (1.5.0) lib/thin/connection.rb:39:in `receive_data'
eventmachine (1.0.0) lib/eventmachine.rb:187:in `run_machine'
eventmachine (1.0.0) lib/eventmachine.rb:187:in `run'
thin (1.5.0) lib/thin/backends/base.rb:63:in `start'
thin (1.5.0) lib/thin/server.rb:159:in `start'
rack (1.4.4) lib/rack/handler/thin.rb:13:in `run'
rack (1.4.4) lib/rack/server.rb:268:in `start'
railties (3.2.11) lib/rails/commands/server.rb:70:in `start'
railties (3.2.11) lib/rails/commands.rb:55:in `block in <top (required)>'
railties (3.2.11) lib/rails/commands.rb:50:in `tap'
railties (3.2.11) lib/rails/commands.rb:50:in `<top (required)>'
script/rails:6:in `require'
script/rails:6:in `<main>'

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.