• This repository has been archived on 11/Jul/2018
  • Stars
    star
    121
  • Rank 293,924 (Top 6 %)
  • Language
    JavaScript
  • Created over 12 years ago
  • Updated over 12 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Example application using Sinatra and the DSL gem to develop web APIs using a simple DSL and a small stack.

Example app using a WebService DSL to write/organize your code.

App Usage

To start the server:

$ bundle install
$ rackup

Or using foreman:

$ foreman start

Or using guard:

$ bundle exec guard

The former two run at http://localhost:9292; the latter runs at http://localhost:4000.

To use the command line:

$ bundle exec irb -Ilib -rbootloader.rb
> Bootloader.start

See/generate the API documentation:

$ rake doc:services

Dependencies

  • A Database (set the connection settings in config/database.yml)
  • Ruby
  • Bundler

Organization

Models live under the models folder, APIs in the API folder. The database.yml file in the config folder contains the db info per environment.

By default all environments share the same settings, but you can drop a custom environment file in the config/environments folder named after the env you want to target.

Migrations are simple ActiveRecord migrations and a seed.rb file is available to pre seed the DB.

Files in the lib folder aren't automatically required.

Writing an API

The DSL for writing an API is straight forward:

    describe_service "hello_world" do |service|
      service.formats   :json
      service.http_verb :get
      service.disable_auth # on by default

      # INPUT
      service.param.string  :name, :default => 'World'

      # OUTPUT
      service.response do |response|
        response.object do |obj|
          obj.string :message, :doc => "The greeting message sent back. Defaults to 'World'"
          obj.datetime :at, :doc => "The timestamp of when the message was dispatched"
        end
      end

      # DOCUMENTATION
      service.documentation do |doc|
        doc.overall "This service provides a simple hello world implementation example."
        doc.param :name, "The name of the person to greet."
        doc.example "<code>curl -I 'http://localhost:9292/hello_world?name=Matt'</code>"
     end

      # ACTION/IMPLEMENTATION
      service.implementation do
        {:message => "Hello #{params[:name]}", :at => Time.now}.to_json
      end

    end

APIs are described in files named the way you want but stored in the API folder. The DSL used comes from the Weasel Diesel gem. It works by defining the end point url (with or without placeholders) and a few key elements of the services:

    describe_service "uri/to/service" do |service|
      service.http_verb :post  # HTTP verb to access this service
      service.disable_auth # disable the auth check (on by default)
      # extra params can be passed to be handled by your code:
      # service.extra[:mobile] = true

      # DOCUMENTATION
      # a documentation block for the service and the request params
      service.documentation do |doc|
        doc.overall "service description"
        doc.param :email, "Description of the param"
        doc.param :password, "Description of the param"
      end

      # INPUT
      # request params, optional unless mentioned othwerwise.
      # if bad params are passed, the request will be returned with a
      # 400 status code. Various data types can be used to cast and
      # check the passed params.
      service.params do |p|
        p.string :email, :required => true
        p.string :password, :required => true
      end

      # OUTPUT
      # response block describing the response sent back to the client.
      # Used to test the services and to document them.
      service.response do |response|
        response.object do |obj|
          obj.string :token, :doc => "The auth token for the authenticated user, only sent back if a callback url isn't sent"
        end
      end

      # ACTION CODE
      # Finally the implementation block being called when the service
      # is reached. The block's returned value is be used as the
      # response's body.
      service.implementation do
        {:foo => :bar}.to_json
      end

      # If you need to define methods to use within this service, you
      # can defined them direcly on the service object ensuring that
      # the method will only be available within this service.
      def service.baz
        :baz
      end

    end

Tests

The app test suite uses a series of helpers wrapping rack/test to test a request going through the stack but without the overhead of actually doing a real HTTP request.

To validate that a service responds as defined in the DSL, you can use the provided helpers, here is an example:

    class HelloWorldTest < MiniTest::Unit::TestCase

      def test_response
        TestApi.get "/hello_world", :name => 'Matt'
        assert_api_response
      end

    end

The TestAPI module dispatches a request to the app and the assert_api_response helper will validate that the response matches the service description.

Look at the test/test_helpers.rb file to see the many other helpers such as TestAPI.last_response, TestAPI.json_response, TestAPI.mobile_get etc...

To automatically re-run tests whilst you are editing your API or your tests:

bundle exec guard

More about the DSL

This app is built on top of Sinatra and the Weasel-Diesel (WebService DSL gem, not related to WSDL). Reasons for a DSL vs the standard Rails approach:

  1. API design becomes the number 1 focus. By focusing on designing the API and documenting it in one unique place, there is no more digging through 5 layers of code. Using the provided tools meant to generate HTML documentation, you can focus on what matters the most: design and communication.

  2. Save development time An API can be designed and even tested (mocked) right away, even before the implementation is done. This is a huge gain of time when dealing with multiple third parties consuming an API. One can spend more time designing and testing against mock data and then finally implement.

  3. Isolation/standalone Each API lives in its own file, it's easily copied over, easy to grasp and easy to see the involved dependencies.

  4. Backend agnostic (potentially language agnostic) Because the DSL is "precompiled" and creates simple objects, the DSL can be plugged on almost any backend (as long as the backend is flexible enough to let you create a route and a function to execute when the route is matched). The implementation doesn't even actually have to be in Ruby, this is far to be done, but I'd love to see the DSLs to be compiled in a language agnostic format to then be loaded by a different backend offering a processing engine for it (i.e: implementation).

  5. Simpler/Easier This goes back to my #1 point, but think about new developers and people wanting to build APIs. Do they really need to know and understand how the router works, enter a route pointing to a controller and a specific action which has a view attached? By simplifying the path, you get better performance (less objects allocated, less GC time, smaller stack), better understanding for all (the entire stack in probably 2k LOC max) and people getting up and running right away.

  6. Documentation Documenting APIs is a pain and it's hard to keep track of changes. Generated documentation usually isn't really good and you want some humans to explain what the service does and how. By offering a compromise of required documentation (incoming param rules), strongly encouraged documentation (response definition used for testing) and suggested documentation (English text for each parts of the request/response), the developer can think of how people will consume the data and can keep the documentation up to date at a pretty low cost. The documentation is then extracted from the DSL and provided in a HTML format.

  7. Security The DSL implementation enforces param verification (name, type, options, length etc...) which provides an extra layer of security for your endpoints. (Remember the GitHub security accident?)

  8. Conventions Because the work surface is smaller, one can more easily encourage conventions and provide "DSL add-ons" for shared features. Also, because the implementation call is a simple Ruby block (with helpers and request context available) it encourages developers to better organize their code.

  9. Stability Because the code base is simple, it doesn't need to be updated often. A new ORM can be added or a new library, but doing that doesn't have to affect the provided "micro framework".

  10. Portability Porting a Rails app to the DSL is actually almost trivial, I even have a a module to point DSLs to controller with actions so the only thing that needs to be changed is the view rendering. (see the WSDSL for the module in question).

  11. Testability Tests can run fast while still going through the stack. Because each test can have access to the entire service description (including the expected response), the amount of automatic tests can be increased, reducing the amount of dev work and assuring that the 3rd party users who built on their code on top of the documentation don't see regressions due to poorly written tests.

  12. Performance This mini-framework is designed to run at optimal speed, a thread pool is set by default and can be tweaked. The amount of objects allocated is reduced to a minimum and because of the small code base optimizations can be done for a given runtime environment.

  13. Freedom Because the implementation of each service is left up to the developer, various ORMs, data stores or libraries can be used without making a radical change to the project. APIs still look the same and all follow the DSL but the implementation is a different concern which can evolve at a different path.

  14. Modularity If an API app grows too much, it is very easy to extract some APIs and move them to a new app. Especially if models are organized in packages and can be shared between applications. (that's a longer discussion, ask me if you want to hear more about that)

  15. Customization Adding new features or standard code paths for all apps is trivial and easy to maintain.

There are very little cons, but let me try to list them nonetheless:

  1. It's not Rails. Rails has decent documentation, people are used to it and it's a well maintained project. The problem though is that most of the documentation isn't to develop APIs, people are used to write Web2.0 websites with Rails and well, most of the new features are HTML related. (streaming, asset pipeline, coffeescript/SCSS) The good news is that we can probably run the same DSL on top of Rails 3.

  2. You can't google it. True, but it should be so easy that you don't need to google it. You can also not google most of what's going in out apps nowadays. Because the code is simple and it is based on well known elements, that shouldn't be a problem.

  3. Deployment. It's Ruby based, it's rack based and it's even Sinatra based. If you can deploy a Ruby web app, you can deploy an app like that, and yes it even works on Heroku.

  4. Maintenance and support. The code shouldn't need maintenance except for bugs being found or new requested features. Because it's just a bootloader (150 LOC w/ empty lines), a layer to implement the DSL on top of Sinatra (100 LOC w/ spaces), some test helpers (130 LOC w/ spaces) and some rake tasks, most of the maintenance and support is actually required on the libraries used such as the web engine (Sinatra), the ORM or the other 3rd party libraries used. At the end of the day, this is just a simple DSL on top of well known, well maintained libraries, and there are no monkey patching going on.

TODO:

  • RSpec helpers/
  • More service examples.
  • Improve documentation templates.
  • Make the ORM configurable.
  • Generators for blank APIs and migrations.
  • Generator to create new apps based on this template.
  • Provide Rack Client as a test alternative to make real HTTP calls and test against a staging environment for instance.
  • Create a test suite that can be run against production to validate a deployment.

More Repositories

1

googlecharts

Ruby Google Chart API
Ruby
699
star
2

Weasel-Diesel

DSL to describe, document and test web services
Ruby
438
star
3

merb-book

Open Source Merb book
Ruby
186
star
4

acts_as_taggable_on_steroids

(OBSOLETE) This plugin is based on acts_as_taggable by DHH but includes extras
Ruby
152
star
5

filebuffer

filebuffer is a package implementing a few file-like interfaces. The implementation is backed by a byte buffer. The main purpose is to have in-memory file alternative.
Go
142
star
6

MacRuby--The-Definitive-Guide

MacRuby: The Definitive Guide - code repo
Ruby
119
star
7

goRailsYourself

A suite of useful functions needed when porting/mixing Go/Rails code.
Go
93
star
8

mimetype-fu

get the mimetype of a file directly in Ruby
Ruby
83
star
9

wd-sinatra

Weasel-Diesel Sinatra app gem, allowing you to generate/update sinatra apps using the Weasel Diesel DSL
Ruby
75
star
10

audio

This is a playground, production ready code is available at https://github.com/go-audio/
Go
74
star
11

goHtmlTemplateExample

Example setting up a Golang web server with static files, HTML templates and Angular.js
Go
67
star
12

globalite

Globalite is meant to be a breed of the best i18n /l10n plugins available for Rails.
Ruby
60
star
13

m3u8Grabber

Experimental command line tool downloading the content of a m3u8 file containing TS video segments and converting them to mkv.
Go
58
star
14

macruby_graphics

Cocoa's Core Graphics & Core Image wrapper for MacRuby (see original author's branch in the link below)
Ruby
57
star
15

ok-go

Google Assistant SDK in Go
Go
57
star
16

GC-stats-middleware

Ruby GC Stats after each request (rack middleware)
50
star
17

fire-and-forget

[Not maintained] Very dumb HTTP "client" that fires requests and doesn't care about the response.
Ruby
44
star
18

ruby-web-search

A Ruby gem that provides a way to retrieve search results via the main search engines using Ruby
Ruby
44
star
19

i18n

Basic internationalization(i18n) library for Ruby
Ruby
36
star
20

LiveTV

Simple OS X application allowing you to watch some live free TV channels (French, English, Italian).
Objective-C
33
star
21

macruby-httpwrapper

simplified http wrapper for MacRuby/RubyMotion using delegation/ruby block
Ruby
29
star
22

phileas_frog

MacRuby video game using Cocoa's CoreAnimation
Ruby
27
star
23

waveform_demo

Flutter Waveform drawing demo repo
Dart
24
star
24

go-web-api-demo

Example implementation of a web API in Go
Go
22
star
25

sm-808

Programming exercise - code the sequencer part of a Drum Machine
21
star
26

merb_babel

Merb Babel is a dead simple translation/localization tool for Merb
Ruby
19
star
27

pluzzdl

My own fork of pluzzdl
Python
17
star
28

noko-vs-builder-benchmark

Rails app to benchmark Nokogiri's XML builder vs Builder
Ruby
15
star
29

WSDSL

Due to a confusing name, this project was renamed and moved to:
Ruby
14
star
30

macruby-json

reimplementation of the ruby json gem for macruby using objc
Ruby
13
star
31

ar-backup

Active Record backup is a Rails plugin which lets you backup your database schema and content in a schema.rb file and fixtures.
Ruby
13
star
32

MacRuby-Siri

Simple demo showing how to use the voice recognition feature of OS X to implement a Siri-like app in MacRuby,
Ruby
13
star
33

macruby-doc-app

simple experiment to query the cocoa and eventually the macruby doc
Ruby
13
star
34

globalite-example

Sample app showing how to implement GlobaLite in a Rails app
Ruby
11
star
35

merb-static-pages-slice

Use markdown files to store and serve static pages (about us, contact us, etc)
Ruby
11
star
36

multibyte

Ruby Multibyte library extracted from ActiveSupport
Ruby
10
star
37

Pvwatts

Wrapper around the http://www.nrel.gov/rredc/pvwatts/ web service API
Ruby
10
star
38

sinatra-wsdsl-example

moved to https://github.com/mattetti/sinatra-web-api-example
JavaScript
10
star
39

TVFrancaise

Simple OS X application allowing you to watch most of the free French TV channels live. (works overseas)
Ruby
10
star
40

apple-remote-wrapper

fork of Martin Kahr work on an Apple Remote Wrapper
Objective-C
8
star
41

IOLI-crackme

crackme exercises with instructions to learn in a safe environment
C++
7
star
42

monkey_patcher

keep track of your monkey patches
7
star
43

google-speech

Fun with Google Cloud Platform speech recognition API
Go
7
star
44

fasthttp

FastHTTP is a Ruby library suitable for use as a drop-in Net::HTTP replacement or with event frameworks like EventMachine and Rev
C
6
star
45

swx-ruby

Ruby implementation of SWX RPC
Ruby
6
star
46

cocoa

Pure Go reimplementation of some Cocoa specific features.
Go
6
star
47

simple_merb_example_app

simple app example, look at the branches to see how to build the app
Ruby
6
star
48

dm-gvideo-adapter

DataMapper adapter for google video
Ruby
6
star
49

herbs

Hopefully Exhaustive Ruby Benchmark Suite
Ruby
5
star
50

merb_training_site

5
star
51

Box2d

My Box2d fork which has the iPhone TestBed compiling under Xcode4
C++
5
star
52

hotocoa-roundtransparentwindow

HotCocoa(MacRuby) port of Apple's RoundTransparentWindow sample using a Nib
5
star
53

MacRuby-Chipmunk

Cocoa Framework designed to use Chipmunk Physics with MacRuby
C
5
star
54

MrStuff

MacRuby experimental wrappers
Ruby
5
star
55

merb-misc

misc stuff related to Merb
Ruby
5
star
56

RubyConfX-code

The code used for my MacRuby talk at RubyConf X | demos: iVoiceControl (voice recognition), PS3 controller + MacRuby + HTML5, tokenization, headless browser and gowalla sample.
JavaScript
5
star
57

sdruby-raffle

Tiny Ruby gem to pick raffle winners
Ruby
4
star
58

merb_latest_rss_items_slice

Just a test Merb Slice displaying the latest RSS items from a feed
Ruby
4
star
59

PS3SixAxis

Use a PS3 Dualshock 3 Controller with Cocoa
JavaScript
4
star
60

drumbeat

Drum beat is a Go library to handle drum beat patterns
Go
4
star
61

ma-agile

CSS
4
star
62

learning_scala

Misc stuff you might care about when learning scala
Scala
4
star
63

plex-web-client

JavaScript
4
star
64

uuid

Go UUID package - use at your own risk
Go
4
star
65

artist_portfolio

Shoes app displaying an artist portfolio using Flickr as a source for content
Ruby
4
star
66

phare

native osx client for lighthouseapp.com
Ruby
4
star
67

mattetti.github.com.old

Matt Aimonetti's home page
JavaScript
4
star
68

merb-book-tmbundle

A TextMate bundle for the merb-book bundle
3
star
69

couchrest-app

CouchApp for CouchRest logger middleware. Browse and analyze your logs directly from Couch
JavaScript
3
star
70

cbc

CBC/radio-canada has great content and in some cases, one might be interested in keeping a backup
Go
3
star
71

MacRuby-NSCollectionView

example for @tronathan
Ruby
3
star
72

bottetti

my irc bot
CoffeeScript
3
star
73

scrapbook

Experiment designed to reflect on the organization of web scraping/data processing.
Ruby
3
star
74

go-exercises

Hopefully fun exercises in Golang. Each exercise has a test suite and your mission to make it pass!
Go
3
star
75

merb-doc

Stuff to generate docs for Merb outside of an app
Ruby
3
star
76

pubnub-publisher

A Ruby publisher gem for PubNub (for those who only care about publishing from Ruby)
Ruby
2
star
77

macruby-raffle

raffle app
Ruby
2
star
78

macruby-roundtransparentwindow

MacRuby port of Apple's RoundTransparentWindow sample
Ruby
2
star
79

sandbox

testing different things, nothing very interesting
Ruby
2
star
80

pastis

fresh and relaxing experimentation
Ruby
2
star
81

misc-Python-experiments

nothing valuable in here, just me playing with Python
Python
2
star
82

dm-websearch-adapter

2
star
83

ruby_practice

series of random exercises to practice Ruby
Ruby
2
star
84

MacRuby-ScriptingBridge-Browser

A ScriptingBridge browser for MacRuby, quite like AppleScript Editor's dictionary browser.
Objective-C
2
star
85

sigen

signature generator written in Ruby and using RMagick
Ruby
2
star
86

merbthor

2
star
87

UnityAzureSpeechSynthesis

Quick demo showing how to use Azure Speech Synthesis (tts) from Unity and loading/playing the generated file in game.
ShaderLab
2
star
88

gvideo

retrieve a google user's list of videos using his user id.
Ruby
2
star
89

LCFF

Luca's Cow Fart Filter
Go
2
star
90

http-proxy-experiment

Simple http proxy server written in Go to learn the language. The goal of this server is to proxy some defined websites with full cookie support.
Go
1
star
91

ruby-exercises

simple katas and exercises prepared for SDRuby meetups.
Ruby
1
star
92

bugsnag-go

bugsnag.com API client in Go
Go
1
star
93

twitter_game_bot

A twitter bot
Ruby
1
star
94

revel_addons

Series of packages for the Revel Go Framework
Go
1
star
95

s2s-auth

s2s auth gem based on ActiveSupport's crypto
Ruby
1
star
96

paca

Selenium IDE test case converter to Go tests to be run via Agouti
Go
1
star
97

wd_sinatra_active_record

ActiveRecord helper for Weasel Diesel Sinatra apps. See https://github.com/mattetti/wd-sinatra
Ruby
1
star
98

Andromeda

The goal is to provide a highly concurrent public facing API that dispatches requests to other APIs via different protocols (HTTP, Thrift..)
Scala
1
star
99

webserver-test-plan

Shell
1
star
100

macruby-twitter_engine

MGTwitterEngine package for MacRuby/HotCocoa
Objective-C
1
star