0.0
No release in over 3 years
API exporter that is easy to understand, extend and use.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

JSON Exporter

Simple to use and extend, versioned, nested objects support, data exporter.

Idea is simple

  • params passed to exporter are available via opts hash (with indifferent access)
  • response is available as hash (with indifferent access)

Installation

gem 'json-exporter'

Full example with all featuters annotated

We will do simple real life API json response formater. It needs to prepare values, filter and format the result.

class ApiExporter < JsonExporter
  before do
    opts[:full] ||= false
  end

  after do
    if json[:email]
      json[:email] = json[:email].downcase
    end
  end
end

class UserExporter < ApiExporter
  define do
    prop :name
    prop :email

    if opts[:full]
      prop :bio, 'Full user bio: %s' % model.bio
    end
  end
end

# usage and response

User = Struct.new(:name, :email, :bio)
user = User.new 'Dux', 'DUX+baz@foo.bar', 'charming chonker'

# UserExporter.export(user)
# -> {name:'Dux', email: 'dux@foo.bar'

# UserExporter.export(user, full: true)
# -> {name:'Dux', email: 'dux@foo.bar', bio: 'Full user bio: %s' % user.bio

Protected methods and variables

opts                      # passed in opts
before                    # run before export, modify opts
after                     # run before export, modify json response
prop or property          # add property to export model
model or @model           # passed in export model
json or @json or response # json response hash direct access
export                    # export other model, quick access

Features in detail

You can read the rspec test and get better insight on look and feel.

class JsonExporter
  # gets converted to
  # def before
  #   super
  #   meta[:version] ||= 1
  # end
  before do
    meta[:version] ||= 1
  end

  define Company do
    # copy :name property
    # same as -> prop :name, model.name
    # same as -> json[:name] = model.name
    prop :name

    # export user as creator property
    prop :creator, export(model.user)
  end

  # define exporter for User class,
  # JsonExporter(User)
  # JsonExporter.export(@user)
  # JsonExporter.new(@user).render
  define User do
    # proparty can be called by full name and can execute block
    prop :calc do
      # access passed @model vi @model or model
      # add
      model.num * 4
    end

    # same as - prop :company, export(model.company)
    # you can pass opts too - prop :company, export(model.company, full: true)
    export :company

    # attact full export of model.company as company_full property
    prop :company_full, export(model.company, full: true)

    # add prop only if opts version is 2+
    if opts.version > 2 do
      prop :extra, :value_1
    end
  end
end

Params in details + examples

  • model that is exported is available via @model or model
  • predefined methods
    • property (or prop) - export single property
    • export - export full model
    • json (or response) - add directly to response hash
  • class block before will define before filter for all objects. Useful to prepare opts before rendering
  • class block after will define after filter for all objects. Useful to add metadata to all objects
class JsonExporter
  # define custom exporter function
  def custom_foo
    if model.respond_to?(:baz)
      param :baz
    end
  end

  # add id and _meta property to all exported objects
  # after filter will run after transformation
  after do
    # every object has an ID, export it
    prop :id

    # call custom exporter function
    custom_foo

    # append medtadata
    json[:_meta] = {
      class: model.class.to_s,
      view_path: model.path,
      api_path: model.api_path
    }
  end

  # same as CompanyUser
  define :company_user do
  end

  define User do
    prop :name, model.name.capitalize

    # export
    export :company      # same as "prop :company, export(model.company)"
    export model.company # same if model.company class name is Company

    # you can add directly to response
    json[:foo]  = @user.foo
  end
end

Custom export names

Define multiple exporters, and pipe same object trough multiple exporters to get different results

class SomeExporter < JsonExporter
  define :foo_1 do
    # ...
  end

  define :foo_2 do
    # ...
  end
end

SomeExporter.export(@model, exporter: :foo_1)
SomeExporter.export(@model, exporter: :foo_2)

Suggested usage

simple cases (should be the usual case)

  • Create base class
  • Export all from there
class PetExporter < JsonExporter
  after do
    # ...
  end

  define Dog do
    # ...
  end
end

PetExporter.export(@dog)

extra exporter per class

If you name class DogPetExporter and export Dog via PetExporter, DogPetExporter class will be used.

class PetExporter < JsonExporter
  after do
    # ...
  end
end

class DogPetExporter < PetExporter
  define do
    # ...
  end
end

PetExporter.export(@dog) or DogPetExporter.export(@dog)

Tips

  • If you want to covert hash keys to string keys, for example to use in Liquid templateing, use @response.stringify_keys
  • If you do not want to use hash with indifferent access for response (https://github.com/dux/hash_wia), set JsonExporter.disable_wia!