Where little posts make a mighty magazine. Where readers become editors. Where giving inspiration is rewarded. From world to
Jan 15, 2015
Where little posts make a mighty magazine. Where readers become editors. Where giving inspiration is rewarded.
From world
to
### Distribute a system message#def find_and_distribute(message_id) message = Message.find(message_id) raise SystemError, "Message not found" if message.nil? message.start_distribution return message.to_jsonrescue SystemError log.error "[API] distribute fail: "#{e.message}”end
1. Beautiful Language: Dynamic, OO, linear
2. Super ORM/ODM: ActiveRecord & Mongoid
Elegant language & environment
class MediaRobotUser include Mongoid::Document
# schema field :rss_url, type: String field :updated, type: DateTime field :update_freq, type: Integer, default: 5
# Validations validates :rss_url, :presence => true validates :update_freq, :presence => true def activate_user robot_queue = ”robot_update_#{self.id.to_s}" Resque.remove_schedule(robot_queue) schedule_options = { 'every' => '15m', 'custom_job_class' => “Bot::Jobs::UpdateMediaRobot", 'queue' => :update_media_robot, 'args' => self.id.to_s, } Resque.set_schedule(robot_queue, schedule_options) end end # class MediaRobotUser
1. Beautiful Language: Dynamic, OO, linear
2. Super ORM/ODM: ActiveRecord & Mongoid
3. Robust libraries: Community focus on libs selected for Rails
Elegant language & environment
1. Beautiful Language: Dynamic, OO, linear
2. Super ORM/ODM: ActiveRecord & Mongoid
3. Robust libraries: Rails gives focus on library development
4. Killer server stack for API: Thin + Rack + Sinatra
Elegant language & environment
Dedicated async web server for Ruby appsInstead of complex Apache/Passenger setup, you download thin and just run your app with it.
Layer between web servers and web frameworksDe facto standard in Ruby world: choose any ruby web server, choose any ruby framework, throw own middleware into soup and it just works!Simple HTTP request handler on top of RackNo MVC bloat for a simple, RESTful API application, but want still use same robust libraries Rails apps use. Just write functionality for your GET /my/api endpoint and go!
# Set Rails session to OAuth2 access token convert middlewareuse Auth::RailsSessionConverter
# Set OAuth2 handleruse Rack::OAuth2::Server
# launch Sinatra REST API serverrequire File.dirname(__FILE__) + '/app/access/restapi'map('/rest/vp5/') { run Access::RESTAPI }
2. Define Rack configuration (e.g. myrack.ru):
3. Run web server:
1. Write Sinatra web app:### API call to get backend version.#get '/system/version' do authenticate!('int_api_user') content_type :json status 200 return (JSON :data_api_version => 'proto v5.0')end
$ bundle exec thin –R myrack.ru –p 9400 -e development start
Ruby ≠ Rails! Sinatra, Resque and many other cool frameworks
Could map other apps to different paths
“homemade” middleware block hooked in!
No fuzz
So.. Let’s think about
Culture for unsecure
code?
Born in 2011 – are
the libs there?
Don’t want that callback
spaghetti!
DB drivers and
ODMs?Performance ?
How to keep running in
production?
/** * Static content delivery */ staticDelivery: function (req, res) { var tmpDir = lzconf.filedbdir + "/tmp"; file.mkdir(tmpDir, '777’, function() { var fileSource = lzconf.filedbdir + "/" + req.params[0]; // do the work easyimg.rescrop(options, function(err, image) { console.log('Resized and cropped: ', image); // send file res.sendfile(tmpTarget, {maxAge: 604800}, function(err) { if (err) { … } // remove temp file fs.unlink(tmpTarget, function(err, success) { …
}); }); }); }); }
function myApiFunc(callback){ /* * This pattern does NOT work! */ try { doSomeAsynchronousOperation(function (err) { if (err) throw (err); /* continue as normal */ }); } catch (ex) { callback(ex); }}
Source: http://www.joyent.com/developers/node/design/errors
Whaaat? Cannot use try…catch??
“Sadly, that seems to be the story of Node.JS and the frameworks that use it. Derby, Meteor, SocketStream– they all are relatively new and immature, and in some cases, lack critical functionality of a web framework or have serious issues with security. That sort of puts me, as a developer, in an odd position. I’ve determined Node.JS is a good platform for a project, but without reinventing the wheel, what framework do I use to speed up development?”
– Andrew Munsellhttps://www.andrewmunsell.com/blog/the-odd-state-of-nodejs-and-its-frameworks
“Sinatra inspired web development framework for node.js – insanely fast, flexible, and simple.”
Yes!
Powered by Connect
Why it rocks
ECMAScript 6: Generators
$ node --harmony> function* giveMeFruit() {... yield "apple";... yield "banana";... }> var fruitMachine = giveMeFruit()> fruitMachine.next(){ value: 'apple', done: false }> fruitMachine.next(){ value: 'banana', done: false }> fruitMachine.next(){ value: undefined, done: true }> fruitMachine.next()Error: Generator has already finished at GeneratorFunctionPrototype.next (native) …
What an earth this has to do with a HTTP framework?
Why it rocks
co – “The ultimate generator based
flow-control goodness for nodejs (supports thunks, promises, etc)”
var co = require('co');
co(function *(){ console.log(“updating user...”); try { var user = yield Users.findOneById(‘123’).exec(); if (!user) throw new Error(“Cannot find user”); user.name = “juha”; yield Promise.promisify(user.save, user)(); } catch (err) { console.log(“Name update failed: ”, err); }})()
This Mongoose finder returns a promise by default
“Save” need to be transferred into promise first for yield
Try…catch as it should be
var koa = require('koa’) , api = require('./v1/myApiModule’), ...
app.use(mount(‘/api/v1’, api));app.listen(8080);
Why it rocks
Additional middleware to handle routing, e.g. koa-mount
Custom middleware upstream & downstream
app.use(function *(next) { var start = new Date; yield next; var ms = new Date - start; this.set('X-Response-Time', ms + 'ms');});
Many needed middleware libs already ported, such as
* koa-session-mongo* koa-passport* koa-cors* koa-send
app.use(session());app.use(passport.username());
app.use(function *(next) { try { yield next; } catch (err) { this.status = err.status || 500; }})
More robust code
var Router = require('koa-router')var API = new Router()
API.get('/topics/:topicId', function *() { try { this.body = yield MySubSystem.getTopic({ id: this.params.topicId, filter: this.request.query.filter_by }); } catch (err) { if (err.name === "DocumentNotFoundError") { this.body = "Topic not found"; this.status = 404; } else { throw err; } }}
Why it rocks