Munson
A JSON API Spec client for Ruby
Installation
Add this line to your application's Gemfile:
gem 'munson'
And then execute:
$ bundle
Or install it yourself as:
$ gem install munson
Usage
Munson::Connection and configuring the default connection
Munson is designed to support multiple connections or API endpoints. A connection is a wrapper around Faraday::Connection that includes a few pieces of middleware for parsing and encoding requests and responses to JSON API Spec.
Munson.configure(url: 'http://api.example.com') do |c|
c.use MyCustomMiddleware
end
Options can be any Faraday::Connection options
Additional connections can be created with:
my_connection = Munson::Connection.new(url: 'http://api2.example.com') do |c|
c.use MoreMiddleware
c.use AllTheMiddlewares
end
Munson::Agent
Munson::Agent provides a small 'DSL' to build requests and parse responses, while allowing additional configuration for a particular 'resource.'
Munson.configure url: 'http://api.example.com'
class Product
def self.munson
return @munson if @munson
@munson = Munson::Agent.new(
connection: Munson.default_connection, # || Munson::Connection.new(...)
paginator: :offset,
type: 'products'
)
end
end
Getting the faraday response
query = Product.munson.filter(min_price: 30, max_price: 65)
# its chainable
query.filter(category: 'Hats').filter(size: ['small', 'medium'])
query.to_params
#=> {:filter=>{:min_price=>"30", :max_price=>"65", :category=>"Hats", :size=>"small,medium"}}
Product.munson.get(params: query.to_params)
Filtering
query = Product.munson.filter(min_price: 30, max_price: 65)
# its chainable
query.filter(category: 'Hats').filter(size: ['small', 'medium'])
query.to_params
#=> {:filter=>{:min_price=>"30", :max_price=>"65", :category=>"Hats", :size=>"small,medium"}}
query.fetch #=> Some lovely data
Sorting
query = Product.munson.sort(created_at: :desc)
# its chainable
query.sort(:price) # defaults to ASC
query.to_params
#=> {:sort=>"-created_at,price"}
query.fetch #=> Some lovely data
Including (Side loading related resources)
query = Product.munson.includes(:manufacturer)
# its chainable
query.includes(:vendor)
query.to_params
#=> {:include=>"manufacturer,vendor"}
query.fetch #=> Some lovely data
Sparse Fieldsets
query = Product.munson.fields(products: [:name, :price])
# its chainable
query.includes(:manufacturer).fields(manufacturer: [:name])
query.to_params
#=> {:fields=>{:products=>"name,price", :manufacturer=>"name"}, :include=>"manufacturer"}
query.fetch #=> Some lovely data
All the things!
query = Product.munson.
filter(min_price: 30, max_price: 65).
includes(:manufacturer).
sort(popularity: :desc, price: :asc).
fields(product: ['name', 'price'], manufacturer: ['name', 'website']).
page(number: 1, limit: 100)
query.to_params
#=> {:filter=>{:min_price=>"30", :max_price=>"65"}, :fields=>{:product=>"name,price", :manufacturer=>"name,website"}, :include=>"manufacturer", :sort=>"-popularity,price", :page=>{:limit=>10}}
query.fetch #=> Some lovely data
Fetching a single resource
Product.munson.find(1)
Paginating
A paged and offset paginator are included with Munson.
Using the offset paginator
class Product
def self.munson
return @munson if @munson
@munson = Munson::Agent.new(
paginator: :offset,
type: 'products'
)
end
end
query = Product.munson.includes('manufacturer').page(offset: 10, limit: 25)
query.to_params
# => {:include=>"manufacturer", :page=>{:limit=>10, :offset=>10}}
query.fetch #=> Some lovely data
Using the paged paginator
class Product
def self.munson
return @munson if @munson
@munson = Munson::Agent.new(
paginator: :paged,
type: 'products'
)
end
end
query = Product.munson.includes('manufacturer').page(page: 10, size: 25)
query.to_params
# => {:include=>"manufacturer", :page=>{:page=>10, :size=>10}}
query.fetch #=> Some lovely data
Custom paginators
Since the JSON API Spec does not dictate how to paginate, Munson has been designed to make adding custom paginators pretty easy.
class CustomPaginator
# @param [Hash] Hash of options like max/default page size
def initialize(opts={})
end
# @param [Hash] Hash to set the 'limit' and 'offset' to be returned later by #to_params
def set(params={})
end
# @return [Hash] Params to be merged into query builder.
def to_params
{ page: {} }
end
end
Munson::Resource
A munson resource provides a DSL in the including class for doing common JSON API queries on your ruby class.
It delegates a set of methods so that they dont have to be accessed through the munson class method and sets a few options based on the including class name.
It also will alter the response objects coming from #fetch and #find. Instead of returning a json hash like when using the bare Munson::Agent, Munson::Resource will pass the JSON Spec attributes and the ID as a hash into your class's initializer.
class Product
include Munson::Resource
register_munson_type :products
end
# Munson method is there, should you be looking for it.
Product.munson #=> Munson::Agent
Setting the type
This will cause Munson to return a hash instead of a class instance (Product).
class Product
include Munson::Resource
munson.type = :products
end
There are two ways to set the JSON API type when using a Munson::Resource
Registering the type
This will cause munson to return your model's datatype. Munson will register this in its type map dictionary and use the class to initialize a model
class Product
include Munson::Resource
register_munson_type :products
end
Filtering
query = Product.filter(min_price: 30, max_price: 65)
# its chainable
query.filter(category: 'Hats').filter(size: ['small', 'medium'])
query.to_params
#=> {:filter=>{:min_price=>"30", :max_price=>"65", :category=>"Hats", :size=>"small,medium"}}
query.fetch #=> Munson::Collection<Product,Product>
Sorting
query = Product.sort(created_at: :desc)
# its chainable
query.sort(:price) # defaults to ASC
query.to_params
#=> {:sort=>"-created_at,price"}
query.fetch #=> Munson::Collection<Product,Product>
Including (Side loading related resources)
query = Product.includes(:manufacturer)
# its chainable
query.includes(:vendor)
query.to_params
#=> {:include=>"manufacturer,vendor"}
query.fetch #=> Munson::Collection<Product,Product>
Sparse Fieldsets
query = Product.fields(products: [:name, :price])
# its chainable
query.includes(:manufacturer).fields(manufacturer: [:name])
query.to_params
#=> {:fields=>{:products=>"name,price", :manufacturer=>"name"}, :include=>"manufacturer"}
query.fetch #=> Munson::Collection<Product,Product>
All the things!
query = Product.
filter(min_price: 30, max_price: 65).
includes(:manufacturer).
sort(popularity: :desc, price: :asc).
fields(product: ['name', 'price'], manufacturer: ['name', 'website']).
page(number: 1, limit: 100)
query.to_params
#=> {:filter=>{:min_price=>"30", :max_price=>"65"}, :fields=>{:product=>"name,price", :manufacturer=>"name,website"}, :include=>"manufacturer", :sort=>"-popularity,price", :page=>{:limit=>10}}
query.fetch #=> Munson::Collection<Product,Product>
Fetching a single resource
Product.find(1) #=> product
Paginating
A paged and offset paginator are included with Munson.
Using the offset paginator
class Product
include Munson::Resource
munson.paginator = :offset
munson. = {default: 10, max: 100}
end
query = Product.includes('manufacturer').page(offset: 10, limit: 25)
query.to_params
# => {:include=>"manufacturer", :page=>{:limit=>10, :offset=>10}}
query.fetch #=> Munson::Collection<Product,Product>
Using the paged paginator
class Product
include Munson::Resource
munson.paginator = :paged
munson. = {default: 10, max: 100}
end
query = Product.includes('manufacturer').page(page: 10, size: 25)
query.to_params
# => {:include=>"manufacturer", :page=>{:page=>10, :size=>10}}
query.fetch #=> Some lovely data
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/coryodaniel/munson.
Munson::Model
WIP see usage
Finding related resources could set instance methods that mix/override behavior of the class level agent...
/articles/1 Article.find(1) uses Article.munson
/articles/1/author article = Article.find(1) uses Article.munson article.author uses article.munson as a new connection that sets the base uri to /articles/1
Usage
address = Address.new
address.state = "FL"
address.save
address.state = "CA"
address.save
# Mind mutex adding method accessors on... dangerous, see her, consider storing them in an @attributes hash...
user = User.find(1)
address = user.addresses.build
address.save #posts on the relation
address = Address.new({...})
address = Address.new
address.assign_attributes
address.update_attributes
address.dirty?
Address.update(1, {}) #update without loading
Address.destroy(1) #Destroy without loading
address = Address.find(300)
address.destroy
Address.find(10)
Address.filter(zip_code: 90210).find(10)
Address.filter(zip_code: 90210).all
Address.filter(zip_code: 90210).fetch
Address.includes(:user, 'user.purchases').filter(active: true).all
Address.includes(:user, 'user.purchases').find(10)
Address.sort(city: :asc)
Address.sort(city: :desc)
Address.fields(:street1, :street2, {user: :name})
Address.fields(:street1, :street2).fields(user: :name)
addresses = Address.fields(:street1, :street2).fields(user: :name)
addresses.first.shipped_products.filter(min_total: 300.00)
Custom collection/member methods?