Well, Here's the background...
I was a low-paid cook and was interested in really cheap ways to get interestingly loaded on the cheap, and be able to buy from my local Chef's Warehouse. Ultra clean nitrous... ... and 70% alcohol by volume (140 proof) bottles of Chartreuse concentrate for 2/3 the price of the watered down stuff. Real Chartreuse too. And all tax deductible in the name of recipe 'experimentation'.
Anyway, I was at the local folk market in Glebe (Sydney, Australia) where I was cooking at a nearby cybercafe.
...Anyway, there's a stand selling 'legal highs'.  Erowid rules!). One of the items on offer is organic wormwood. $7 Australian a bag... can't remember the weight but it was far more than I needed to make one batch of the supposedly sinful slippery green 'sinthe.
I was spurred to do this by reading a book called 'Bigger Secrets' by William Poundstone. In it he reveals the most likely recipe for the famous 'Oysters Rockefeller' (apologies to the purists). One of the ingredients when the recipe was first developed in 1899 by Jules Alciatore, of Antoine's in New Orleans was Absinthe.
Turns out that Absinthe has been known in New Orleans since 1916 for causing 'Shocking Behaviour'. Posession of Absinthe in a commercial establishment risks serious fines and license investigations. Specifically, it was the wormwood in absinthe that was outlawed, as it was held to be the 'effective ingredient'.
A replacement, called Herbsaint, was soon developed that met the laws by having a similar taste and quality but omitting the illegal wormwood. This has been what Antoine's has used in place of Absinthe in their green oysters since 1916... and believe me, they're fussy....
So if you buy Absinthe in New Orleans, be prepared to be served Herbsaint. Any bar owner/manager who actually had absinthe on the premises would not serve it to just anyone off the street...
Anyway, I researched a recipe for absinthe using wormwood (not easy without the internet... potentially humanity's greatest learning and unifying tool... it's NOT just a marketing tool!).
I steeped about 2-3 tablespoons of wormwood root chips in a 375 ml bottle of Gin in a cool, dark place for about 2 months. The big day came and I strained the evil-smelling brew with fine cheesecloth. The smell was indescribable. No, I mean it, I'm not going to even try.
There was only one thing to do. I waited for the next full moon and lit some massive beeswax candles for atmosphere in my cramped garret room in inner-city Redfern.
The moment arrived. I plonked a couple of caster sugar cubes in a tea strainer over a chilled, thick-walled 150 ml lowball tumbler. I gently inverted 50ml of the 'sinthe and 100ml of chilled spring water with a generous handful of solid, diamond-clear fist-sized ice cubes in a shaker, then slowly poured the decoction through the glistening cubes. They melted resonably well, though not so well as I had thought.
I lifted the glass and eyed the still-clear fluid. Oh well, I never really took to Ouzo either. Na Zdrowie.
The shot went down smooth and I kept my pharynx closed just in case. Then as I began to breathe 5 or 10 seconds later the taste hit.
It was the bitterest thing I have EVER tasted. I mean it took a physical effort to choke down even the smallest amount, it was that foul. I could have drunk autopsy runoff with less effort. Hell, for an alcoholic, would be to be trapped for eternity with a million bottles of direct-extract absinthe... full of alcohol but tasting like the bitterest beer times one thousand... undrinkable.
I soldiered through a total of 200mls, diluted as above. I felt drunk and a little buzzy, but no psychoactive acivity. The convulsive heaving as my autonomous reflexes fought my every conscious effort to drink it was interesting... absinthe's 'Shocking Behaviour' is most likely hair-trigger vomit reflexes. I mean it. My experiment with absinthe was one of only two times in my whole life I've ever thrown away alcohol. I mean, I even kept and consumed the 'Green Death', a jelly I made with the aforementioned 140 proof Chartreuse concentrate and lime jelly crystals (tip for would be alcoholic jelly makers: use double the crystals but the same amount of boiling water, wait till cooler than body temperature, add about half again as much of the 70% alcohol concentrate. Cool as quickly as possible without freezing.)
But absinthe? You can keep it. For pure indulgence, try sucking on a phat bud that's been cooked gently in isopropyl alcohol 70%, butter and maple syrup for 15 mins in a pressure cooker. Not much active ingredient but wow, what a taste.
But that's a story for another time. I'd like to close by saying that plant psychoactives are not evil, unlike those that seek to perpetuate political power by cracking down on nature's most beneficial plants. They are the real evil.
Bear
Formerly of Sydney, Australia
Chef- Raconteur- Radical
Experience Reports are the writings and opinions of the individual authors who submit them.
Some of the activities described are dangerous and/or illegal and none are recommended by Erowid.


|
JavaTM 2 Platform Standard Ed. 5.0 |
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
public interface Attributes
This interface represents a collection of attributes.
In a directory, named objects can have associated with them attributes. The Attributes interface represents a collection of attributes. For example, you can request from the directory the attributes associated with an object. Those attributes are returned in an object that implements the Attributes interface.
Attributes in an object that implements the Attributes interface are unordered. The object can have zero or more attributes. Attributes is either case-sensitive or case-insensitive (case-ignore). This property is determined at the time the Attributes object is created. (see BasicAttributes constructor for example). In a case-insensitive Attributes, the case of its attribute identifiers is ignored when searching for an attribute, or adding attributes. In a case-sensitive Attributes, the case is significant.
Note that updates to Attributes (such as adding or removing an attribute) do not affect the corresponding representation in the directory. Updates to the directory can only be effected using operations in the DirContext interface.
DirContext.getAttributes(javax.naming.Name)
,
DirContext.modifyAttributes(javax.naming.Name, int, javax.naming.directory.Attributes)
,
DirContext.bind(javax.naming.Name, java.lang.Object, javax.naming.directory.Attributes)
,
DirContext.rebind(javax.naming.Name, java.lang.Object, javax.naming.directory.Attributes)
,
DirContext.createSubcontext(javax.naming.Name, javax.naming.directory.Attributes)
,
DirContext.search(javax.naming.Name, javax.naming.directory.Attributes, java.lang.String[])
,
BasicAttributes
| Method Summary | |
|---|---|
Object |
clone()
Makes a copy of the attribute set. |
Attribute |
get(String attrID)
Retrieves the attribute with the given attribute id from the attribute set. |
NamingEnumeration<? extends Attribute> |
getAll()
Retrieves an enumeration of the attributes in the attribute set. |
NamingEnumeration<String> |
getIDs()
Retrieves an enumeration of the ids of the attributes in the attribute set. |
boolean |
isCaseIgnored()
Determines whether the attribute set ignores the case of attribute identifiers when retrieving or adding attributes. |
Attribute |
put(Attribute attr)
Adds a new attribute to the attribute set. |
Attribute |
put(String attrID,
Object val)
Adds a new attribute to the attribute set. |
Attribute |
remove(String attrID)
Removes the attribute with the attribute id 'attrID' from the attribute set. |
int |
size()
Retrieves the number of attributes in the attribute set. |
| Method Detail |
|---|
boolean isCaseIgnored()
int size()
Attribute get(String attrID)
attrID
- The non-null id of the attribute to retrieve.
If this attribute set ignores the character
case of its attribute ids, the case of attrID
is ignored.
put(java.lang.String, java.lang.Object)
,
remove(java.lang.String)
NamingEnumeration<? extends Attribute> getAll()
NamingEnumeration<String> getIDs()
Attribute put(String attrID, Object val)
attrID
- non-null The id of the attribute to add.
If the attribute set ignores the character
case of its attribute ids, the case of attrID
is ignored.val
- The possibly null value of the attribute to add.
If null, the attribute does not have any values.
remove(java.lang.String)
Attribute put(Attribute attr)
attr
- The non-null attribute to add.
If the attribute set ignores the character
case of its attribute ids, the case of
attr's identifier is ignored.
remove(java.lang.String)
Attribute remove(String attrID)
attrID
- The non-null id of the attribute to remove.
If the attribute set ignores the character
case of its attribute ids, the case of
attrID is ignored.
Object clone()
|
JavaTM 2 Platform Standard Ed. 5.0 |
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
Copyright © 2004, 2010 Oracle and/or its affiliates. All rights reserved. Use is subject to license terms. Also see the documentation redistribution policy.


Building a RESTful API with Koa and Postgres
In this tutorial, you’ll learn how to develop a RESTful API with Koa 2 and Postgres. You’ll also be taking advantage of async/await functions, from ES2017, and test driven development (TDD).
This tutorial requires Node v7.6.0 or greater.
Parts
This article is part of a 4-part Koa and Sinon series…
- Building a RESTful API with Koa and Postgres (this article)
- Stubbing HTTP Requests with Sinon
- User Authentication with Passport and Koa
- Stubbing Node Authentication Middleware with Sinon
NPM Dependencies
- Koa v2.3.0
- Mocha v3.5.0
- Chai v4.1.1
- Chai HTTP v3.0.0
- Knex v0.13.0
- pg v7.1.2
- koa-router v7.2.1
- koa-bodyparser v4.2.0
Contents
Objectives
By the end of this tutorial, you will be able to…
- Set up a project with Koa using test driven development
- Write schema migration files with Knex to create new database tables
- Generate database seed files with Knex and apply the seeds to the database
- Set up the testing structure with Mocha and Chai
- Perform the basic CRUD functions on a RESTful resource with Knex methods
- Create a CRUD app, following RESTful best practices
- Write integration tests
- Write tests, and then write just enough code to pass the tests
- Create routes with Koa Router
- Parse the request body with koa-bodyparser
Getting Started
What are we building?
Your goal is to design a RESTful API, using test driven development, for a single resource - movies
. The API itself should follow RESTful design principles, using the basic HTTP verbs: GET, POST, PUT, and DELETE.
What is Koa?
Koa is a web framework for Node.js.
Although it’s designed by the same team that created Express, it’s much lighter than Express though - so it comes with very little out of the box. It’s really just a tiny wrapper on top of Node’s HTTP module. Koa allows you - the developer - to pick and choose the tools you want to use from the community.
It has native support for async/await, which makes it easier and faster to develop an API since you don’t have to deal with callbacks and callback hell.
Finally, since Koa has similar patterns to Express, it’s relatively easy to pick up if you’ve worked at all with Express.
NOTE: For more, review Koa vs Express.
TDD
Test Driven Development (TDD) is an iterative development cycle that emphasizes writing automated tests before writing the actual code.
Why?
- Helps break down problems into manageable pieces since you should have a better understanding of what you’re going to write
- Forces you to write cleaner code
- Prevents over coding
Red-Green-Refactor
TDD often follows the “Red-Green-Refactor” development cycle:
- RED: Write a test, which should fail when you run it
- GREEN: Write just enough code for the test to pass
- REFACTOR: Refactor code and retest, again and again (if necessary)
Project Setup
Start by cloning down the base project:
$ git clone https://github.com/mjhea0/node-koa-api \
--branch v1 --single-branch
$ cd node-koa-api
Then, check out the v1 tag to the master branch and install the dependencies:
$ git checkout tags/v1 -b master
$ npm install
Run two quick sanity checks to make sure all is well:
$ npm start
It works!
$ npm test
Sample Test
✓ should pass
1 passing (8ms)
Take a quick look at the project structure before moving on.
Koa
As always, we’ll begin with the obligatory hello world. But first, since we’re following TDD, let’s write a quick test.
Install Chai HTTP so we can test HTTP calls:
$ npm install<EMAIL_ADDRESS>--save-dev
Create a new file in the “test” directory called routes.index.test.js:
process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
const server = require('../src/server/index');
describe('routes : index', () => {
describe('GET /', () => {
it('should return json', (done) => {
chai.request(server)
.get('/')
.end((err, res) => {
should.not.exist(err);
res.status.should.eql(200);
res.type.should.eql('application/json');
res.body.status.should.equal('success');
res.body.message.should.eql('hello, world!');
done();
});
});
});
});
So, within the second describe
block, we have a single it
statement, which defines a test case. In this simple case, we’re testing the response from a GET request to the main route, /
.
Run the test via npm test
. You should see the following error since the server is not setup:
TypeError: app.address is not a function
Next, let’s stand up a quick Koa server. Install Koa:
$ npm install<EMAIL_ADDRESS>--save
Then, update src/server/index.js like so:
const Koa = require('koa');
const app = new Koa();
const PORT = 1337;
app.use(async (ctx) => {
ctx.body = {
status: 'success',
message: 'hello, world!'
};
});
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
Here, we created a new instance of Koa and then mounted a basic async function to the app. This function takes the Koa context as a parameter, ctx
. It’s worth noting that this object encapsulates both the Node request and response objects. We then set the return value to ctx.body
, which will be sent back as the response body when user hits any route.
Run the Koa server, via npm start
, and then navigate to http://localhost:1337/. You should see:
{
"status": "success",
"message": "hello, world!"
 They should now pass.
Database
Moving right along, download and install Postgres, if you don’t already have it, and then fire up the server on port 5432.
Along with Postgres, we’ll use pg and Knex to interact with the database itself:
$ npm install<EMAIL_ADDRESS><EMAIL_ADDRESS>--save
Install Knex globally as well so you can use the CLI tool:
$ npm install<EMAIL_ADDRESS>-g
Next, we need to create two new databases, one for our development environment and the other for test environment.
Open psql in the terminal, and create the databases:
$ psql
psql (9.6.1)
# CREATE DATABASE koa_api;
CREATE DATABASE
# CREATE DATABASE koa_api_test;
CREATE DATABASE
# \q
With that, we can now initialize Knex.
Knex
Run knex init
in the project root to initialize a new config file called knexfile.js. Override the default info with:
const path = require('path');
const BASE_PATH = path.join(__dirname, 'src', 'server', 'db');
module.exports = {
test: {
client: 'pg',
connection: 'postgres://username:password@localhost:5432/koa_api_test',
migrations: {
directory: path.join(BASE_PATH, 'migrations')
},
seeds: {
directory: path.join(BASE_PATH, 'seeds')
}
},
development: {
client: 'pg',
connection: 'postgres://username:password@localhost:5432/koa_api',
migrations: {
directory: path.join(BASE_PATH, 'migrations')
},
seeds: {
directory: path.join(BASE_PATH, 'seeds')
}
}
};
NOTE: Make sure to replace
username
andpassword
with your database username and password, respectively.
Next, let’s create a new migration to define the database schema:
$ knex migrate:make movies
This created a “src/server/db/migrations” folder with a timestamped migration file. Update the file like so:
exports.up = (knex, Promise) => {
return knex.schema.createTable('movies', (table) => {
table.increments();
table.string('name').notNullable().unique();
table.string('genre').notNullable();
table.integer('rating').notNullable();
table.boolean('explicit').notNullable();
});
};
exports.down = (knex, Promise) => {
return knex.schema.dropTable('movies');
};
Add a new file to the “db” folder called connection.js to, well, connect to the database using the appropriate Knex configuration based on the environment (development, test, staging, production, etc.):
const environment = process.env.NODE_ENV || 'development';
const config = require('../../../knexfile.js')[environment];
module.exports = require('knex')(config);
Apply the migration to the development database:
$ knex migrate:latest --env development
Next, let’s create a seed file to populate the database with some initial data:
$ knex seed:make movies_seed
This added a seed file to “src/server/db/seeds”; update it to match the database schema:
exports.seed = (knex, Promise) => {
return knex('movies').del()
.then(() => {
return knex('movies').insert({
name: 'The Land Before Time',
genre: 'Fantasy',
rating: 7,
explicit: false
});
})
.then(() => {
return knex('movies').insert({
name: 'Jurassic Park',
genre: 'Science Fiction',
rating: 9,
explicit: true
});
})
.then(() => {
return knex('movies').insert({
name: 'Ice Age: Dawn of the Dinosaurs',
genre: 'Action/Romance',
rating: 5,
explicit: false
});
});
};
Apply the seed:
$ knex seed:run --env development
Finally, hop back into psql to ensure the database has been updated:
$ psql
psql (9.6.1)
# \c koa_api
You are now connected to database "koa_api".
# select * from movies;
id | name | genre | rating | explicit
----+--------------------------------+-----------------+--------+----------
1 | The Land Before Time | Fantasy | 7 | f
2 | Jurassic Park | Science Fiction | 9 | t
3 | Ice Age: Dawn of the Dinosaurs | Action/Romance | 5 | f
(3 rows)
# \q
Koa Router
Unlike Express, Koa does not provide any routing middleware. There are a number of options available, but we’ll use koa-router due to its simplicity.
$ npm install<EMAIL_ADDRESS>--save
Create a new folder called “routes” within “server”, and then add an index.js file to it:
const Router = require('koa-router');
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = {
status: 'success',
message: 'hello, world!'
};
})
module.exports = router;
Then, update src/server/index.js:
const Koa = require('koa');
const indexRoutes = require('./routes/index');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(indexRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
Essentially, we moved the /
route out of the main application file. Ensure the tests still pass before moving on.
Routes
Again, we’ll take a test-first approach to writing our routes:
| URL | HTTP Verb | Action |
|---|---|---|
| /api/v1/movies | GET | Return ALL movies |
| /api/v1/movies/:id | GET | Return a SINGLE movie |
| /api/v1/movies | POST | Add a movie |
| /api/v1/movies/:id | PUT | Update a movie |
| /api/v1/movies/:id | DELETE | Delete a movie |
Before diving in, let’s add some structure. First, add a new folder called “queries” to the “db” folder, and then add a file called movies.js to that newly created folder:
const knex = require('../connection');
We’ll add the database queries associated with the movies
resource to this file. Next, add a new route file called movies.js to “routes”:
const Router = require('koa-router');
const queries = require('../db/queries/movies');
const router = new Router();
const BASE_URL = `/api/v1/movies`;
module.exports = router;
Then, wire this file up to the main application in src/server/index.js:
const Koa = require('koa');
const indexRoutes = require('./routes/index');
const movieRoutes = require('./routes/movies');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(indexRoutes.routes());
app.use(movieRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
Finally, add a new test file to “test” called routes.movies.test.js:
process.env.NODE_ENV = 'test';
const chai = require('chai');
const should = chai.should();
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
const server = require('../src/server/index');
const knex = require('../src/server/db/connection');
describe('routes : movies', () => {
beforeEach(() => {
return knex.migrate.rollback()
.then(() => { return knex.migrate.latest(); })
.then(() => { return knex.seed.run(); });
});
afterEach(() => {
return knex.migrate.rollback();
});
});
So, when the tests are ran, the beforeEach()
is fired before any of test specs, applying the migrations to the test database. After the specs run, the database is rolled back to a pristine state in the afterEach()
.
With that, let’s add our routes!
GET All Movies
Start with a test:
describe('GET /api/v1/movies', () => {
it('should return all movies', (done) => {
chai.request(server)
.get('/api/v1/movies')
.end((err, res) => {
// there should be no errors
should.not.exist(err);
// there should be a 200 status code
res.status.should.equal(200);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "success"}
res.body.status.should.eql('success');
// the JSON response body should have a
// key-value pair of {"data": [3 movie objects]}
res.body.data.length.should.eql(3);
// the first object in the data array should
// have the right keys
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
done();
});
});
});
Take note of the code comments. Review Testing Node.js With Mocha and Chai for more info. Run the test to make sure it fails:
Uncaught AssertionError: expected [Error: Not Found] to not exist
To get the test to pass, add the route handler to src/server/routes/movies.js:
router.get(BASE_URL, async (ctx) => {
try {
const movies = await queries.getAllMovies();
ctx.body = {
status: 'success',
data: movies
};
} catch (err) {
console.log(err)
}
})
Add the DB query to src/server/db/queries/movies.js:
const knex = require('../connection');
function getAllMovies() {
return knex('movies')
.select('*');
}
module.exports = {
getAllMovies
};
So, getAllMovies()
returns a promise object. Then, within the async
function, execution stops at the await
. Execution continues once the promise is resolved.
Run the tests to ensure they pass:
routes : index
GET /
✓ should return json
routes : movies
GET /api/v1/movies
✓ should return all movies
Sample Test
✓ should pass
3 passing (177ms)
GET Single Movie
What if we just want a single movie?
describe('GET /api/v1/movies/:id', () => {
it('should respond with a single movie', (done) => {
chai.request(server)
.get('/api/v1/movies/1')
.end((err, res) => {
// there should be no errors
should.not.exist(err);
// there should be a 200 status code
res.status.should.equal(200);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "success"}
res.body.status.should.eql('success');
// the JSON response body should have a
// key-value pair of {"data": 1 movie object}
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
done();
});
});
});
Make sure the test fails, and then add the route handler:
router.get(`${BASE_URL}/:id`, async (ctx) => {
try {
const movie = await queries.getSingleMovie(ctx.params.id);
ctx.body = {
status: 'success',
data: movie
};
} catch (err) {
console.log(err)
}
})
Add the query as well:
function getSingleMovie(id) {
return knex('movies')
.select('*')
.where({ id: parseInt(id) });
}
Don’t forget to export it:
module.exports = {
getAllMovies,
getSingleMovie,
};
The test should now pass. Before moving on though, what happens if the movie ID does not exist? Start with a test to find out.
Add an it
block to the previous describe
block:
it('should throw an error if the movie does not exist', (done) => {
chai.request(server)
.get('/api/v1/movies/9999999')
.end((err, res) => {
// there should an error
should.exist(err);
// there should be a 404 status code
res.status.should.equal(404);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "error"}
res.body.status.should.eql('error');
// the JSON response body should have a
// key-value pair of {"message": "That movie does not exist."}
res.body.message.should.eql('That movie does not exist.');
done();
});
});
Make sure the test fails before updating the code:
router.get(`${BASE_URL}/:id`, async (ctx) => {
try {
const movie = await queries.getSingleMovie(ctx.params.id);
if (movie.length) {
ctx.body = {
status: 'success',
data: movie
};
} else {
ctx.status = 404;
ctx.body = {
status: 'error',
message: 'That movie does not exist.'
};
}
} catch (err) {
console.log(err)
}
})
The test should now pass.
POST
How about adding a new movie to the database?
describe('POST /api/v1/movies', () => {
it('should return the movie that was added', (done) => {
chai.request(server)
.post('/api/v1/movies')
.send({
name: 'Titanic',
genre: 'Drama',
rating: 8,
explicit: true
})
.end((err, res) => {
// there should be no errors
should.not.exist(err);
// there should be a 201 status code
// (indicating that something was "created")
res.status.should.equal(201);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "success"}
res.body.status.should.eql('success');
// the JSON response body should have a
// key-value pair of {"data": 1 movie object}
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
done();
});
});
});
Koa does not parse the request body by default, so we need to add middleware for body parsing. koa-bodyparser is a popular choice:
$ npm install<EMAIL_ADDRESS>--save
Add the requirement to src/server/index.js, and then make sure to mount the middleware to the app before the routes:
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const indexRoutes = require('./routes/index');
const movieRoutes = require('./routes/movies');
const app = new Koa();
const PORT = process.env.PORT || 1337;
app.use(bodyParser());
app.use(indexRoutes.routes());
app.use(movieRoutes.routes());
const server = app.listen(PORT, () => {
console.log(`Server listening on port: ${PORT}`);
});
module.exports = server;
Add the route handler:
router.post(`${BASE_URL}`, async (ctx) => {
try {
const movie = await queries.addMovie(ctx.request.body);
if (movie.length) {
ctx.status = 201;
ctx.body = {
status: 'success',
data: movie
};
} else {
ctx.status = 400;
ctx.body = {
status: 'error',
message: 'Something went wrong.'
};
}
} catch (err) {
console.log(err)
}
})
DB query:
function addMovie(movie) {
return knex('movies')
.insert(movie)
.returning('*');
}
What if the payload does not include the correct keys? Add a new it
block:
it('should throw an error if the payload is malformed', (done) => {
chai.request(server)
.post('/api/v1/movies')
.send({
name: 'Titanic'
})
.end((err, res) => {
// there should an error
should.exist(err);
// there should be a 400 status code
res.status.should.equal(400);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "error"}
res.body.status.should.eql('error');
// the JSON response body should have a message key
should.exist(res.body.message);
done();
});
});
Then update the route handler:
router.post(`${BASE_URL}`, async (ctx) => {
try {
const movie = await queries.addMovie(ctx.request.body);
if (movie.length) {
ctx.status = 201;
ctx.body = {
status: 'success',
data: movie
};
} else {
ctx.status = 400;
ctx.body = {
status: 'error',
message: 'Something went wrong.'
};
}
} catch (err) {
ctx.status = 400;
ctx.body = {
status: 'error',
message: err.message || 'Sorry, an error has occurred.'
};
}
})
PUT
Test:
describe('PUT /api/v1/movies', () => {
it('should return the movie that was updated', (done) => {
knex('movies')
.select('*')
.then((movie) => {
const movieObject = movie[0];
chai.request(server)
.put(`/api/v1/movies/${movieObject.id}`)
.send({
rating: 9
})
.end((err, res) => {
// there should be no errors
should.not.exist(err);
// there should be a 200 status code
res.status.should.equal(200);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "success"}
res.body.status.should.eql('success');
// the JSON response body should have a
// key-value pair of {"data": 1 movie object}
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
// ensure the movie was in fact updated
const newMovieObject = res.body.data[0];
newMovieObject.rating.should.not.eql(movieObject.rating);
done();
});
});
});
});
Route handler:
router.put(`${BASE_URL}/:id`, async (ctx) => {
try {
const movie = await queries.updateMovie(ctx.params.id, ctx.request.body);
if (movie.length) {
ctx.status = 200;
ctx.body = {
status: 'success',
data: movie
};
} else {
ctx.status = 404;
ctx.body = {
status: 'error',
message: 'That movie does not exist.'
};
}
} catch (err) {
ctx.status = 400;
ctx.body = {
status: 'error',
message: err.message || 'Sorry, an error has occurred.'
};
}
})
DB query:
function updateMovie(id, movie) {
return knex('movies')
.update(movie)
.where({ id: parseInt(id) })
.returning('*');
}
Did you notice that we are already handling a case where the movie does not exist in the route handler? Let’s add a test for that:
it('should throw an error if the movie does not exist', (done) => {
chai.request(server)
.put('/api/v1/movies/9999999')
.send({
rating: 9
})
.end((err, res) => {
// there should an error
should.exist(err);
// there should be a 404 status code
res.status.should.equal(404);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "error"}
res.body.status.should.eql('error');
// the JSON response body should have a
// key-value pair of {"message": "That movie does not exist."}
res.body.message.should.eql('That movie does not exist.');
done();
});
});
DELETE
Test:
describe('DELETE /api/v1/movies/:id', () => {
it('should return the movie that was deleted', (done) => {
knex('movies')
.select('*')
.then((movies) => {
const movieObject = movies[0];
const lengthBeforeDelete = movies.length;
chai.request(server)
.delete(`/api/v1/movies/${movieObject.id}`)
.end((err, res) => {
// there should be no errors
should.not.exist(err);
// there should be a 200 status code
res.status.should.equal(200);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "success"}
res.body.status.should.eql('success');
// the JSON response body should have a
// key-value pair of {"data": 1 movie object}
res.body.data[0].should.include.keys(
'id', 'name', 'genre', 'rating', 'explicit'
);
// ensure the movie was in fact deleted
knex('movies').select('*')
.then((updatedMovies) => {
updatedMovies.length.should.eql(lengthBeforeDelete - 1);
done();
});
});
});
});
it('should throw an error if the movie does not exist', (done) => {
chai.request(server)
.delete('/api/v1/movies/9999999')
.end((err, res) => {
// there should an error
should.exist(err);
// there should be a 404 status code
res.status.should.equal(404);
// the response should be JSON
res.type.should.equal('application/json');
// the JSON response body should have a
// key-value pair of {"status": "error"}
res.body.status.should.eql('error');
// the JSON response body should have a
// key-value pair of {"message": "That movie does not exist."}
res.body.message.should.eql('That movie does not exist.');
done();
});
});
});
Route handler:
router.delete(`${BASE_URL}/:id`, async (ctx) => {
try {
const movie = await queries.deleteMovie(ctx.params.id);
if (movie.length) {
ctx.status = 200;
ctx.body = {
status: 'success',
data: movie
};
} else {
ctx.status = 404;
ctx.body = {
status: 'error',
message: 'That movie does not exist.'
};
}
} catch (err) {
ctx.status = 400;
ctx.body = {
status: 'error',
message: err.message || 'Sorry, an error has occurred.'
};
}
})
function deleteMovie(id) {
return knex('movies')
.del()
.where({ id: parseInt(id) })
.returning('*');
}
Run the tests to ensure all pass:
routes : index
GET /
✓ should return json
routes : movies
GET /api/v1/movies
✓ should return all movies
GET /api/v1/movies/:id
✓ should respond with a single movie
✓ should throw an error if the movie does not exist
POST /api/v1/movies
✓ should return the movie that was added
✓ should throw an error if the payload is malformed
PUT /api/v1/movies
✓ should return the movie that was updated
✓ should throw an error if the movie does not exist
DELETE /api/v1/movies/:id
✓ should return the movie that was deleted
✓ should throw an error if the movie does not exist
Sample Test
✓ should pass
11 passing (697ms)
Next Steps
With that, you now have a basic Koa RESTful API up and running.
Test your knowledge by adding additional test cases and error handlers to cover anything missed. You may also want to convert an existing Express app over to Koa. Check out the Koa Examples repo for more code examples.
Add end-to-end tests with TestCafe.
This tutorial took advantage of async/await functions in Koa version 2. If you’re interested in comparing this pattern to the generator pattern found in Koa 1, review the code in the Koa API repo.
Finally, check out the following posts that build on the Koa app built in this post:
- Stubbing HTTP Requests with Sinon
- User Authentication with Passport and Koa
- Stubbing Node Authentication Middleware with Sinon
Grab the final code from the v2 tag of node-koa-api repo. There’s slides as well.
Please add questions and/or comments below. Cheers!


{% if admin == true %}
ADMIN
{% endif %}
SEARCH
RATE
FIND GEN-EDS
ABOUT US
{%if user == None%}
LOGIN
{%else%}
{{user}}
LOGOUT
{%endif%}
Rate Form
Please fill out all necessary fields to rate your course
Professor
{% for Professor in Professors %}
{{Professor}}
{% endfor %}
Valid Professor name is required.
Subject
Valid subject is required.
Course Number
Valid course number is required.
Rating out of 10
Value:
Hours per Week Spent
Value:
Comment
Comment cannot be blank or exceed X characters
Submit Rating


%- include views/head -%>
<%- include views/header -%> <%- include views/section_installation -%>
<%- include views/section_options -%>
<%- include views/section_html_examples -%>
<%- include views/section_js_examples -%>
<%- include views/footer -%>


Asset Management
Add Employee
Delete Employee
Edit
Employees
Users
Edit
First Name
Last Name
Edit
{{ user.fName }}
{{ user.lName }}
Create New User
Create New User:
Edit User:
First Name:
Last Name:
Age:
Employee Id:
E-mail Id:
ADD


Featured Artist
Featured Artist
Spice Girls premiere new song
Headlines (Friendship Never Ends) is the first new single from the reformed girl band since 2000 and is the official Children In Need track for 2007.
Geri Halliwell, Victoria Beckham, Melanie Brown, Melanie Chisholm and Emma Bunton have regrouped to promote a new Spice Girls' greatest hits album and an upcoming world tour. more ...
Jay-Z defends Nas album title
Hip-hop mogul Jay-Z has defended Nas' decision to name his new album N**ger, but insists the naming of the record is "misguided".
Jay-Z - real name Shawn Carter - is releasing the LP under his label Island Def Jam Music Group, but admits he is not fond of its title. more ...
Winehouse defends MTV EMAs performance
Amy Winehouse has defended her performance at Thursday's MTV European Music Awards - citing exhaustion for her bizarre behaviour. more ...
Green Day back Britney
Billie Joe Armstrong claims Britney Spears is "a manufactured child", who has been "thrown to the lions" by the world's media.
The Green Day singer has expressed his anger at the treatment the pop icon has been receiving in recent months, following her September appearance at the MTV Awards. more ...


|
|
Every brand has a story. At Brand Applause, we help you develop the interactive tools to share yours with the world.
| Skin IQ | ||
The simplicity of working with Skin IQ's Microclimate Manager are braught out in HD movies that play at trade shows and over the web. |
| USA Gymnastics | ||
| Click here to see a how USA Gymnastics dramatize the importance of supporting our Olympic athletes. |
| Dräger: A Breath Ahead | ||
For Dräger we created 40 hours of curriculum training to cover the latest neonatal techniques and industry trends - positioning Dräger as the leader in respirator care. |
| Web-Based Application Training | ||
Boost brand loyalty by creating simple, engaging, on-line training that allows customers to become experts in your software or on-line resources. |
| ACE | ||
| For ACE insurance we produce diverse videos to promote and position their range of insurance products and services for businesses and individuals. |
| Secret Service | ||
| We help the Secret Service dramatize how to customers can protect themselves from crime such as True Name Fraud. |
| Webcasting | ||
| We produce full-feature interactive webcasts which allow you to communicate with audiences around the world. |
|
|
|
|
|||||
|
© Copyright Brand Applause 2013. All rights reserved.
|


@ThreadSafe public interface ContentDeliveryConfig
Content Delivery Units
)
and other information for the targeted profile.| Modifier and Type | Field and Description |
|---|---|
static String |
SMOOKS_VISITORS_SORT
|
| Modifier and Type | Method and Description |
|---|---|
void |
addToExecutionLifecycleSets()
Add the execution lifecycle sets for the configuration.
|
void |
executeHandlerCleanup(ExecutionContext executionContext)
Cleanup execution context lifecycle aware handlers.
|
void |
executeHandlerInit(ExecutionContext executionContext)
Initialize execution context lifecycle aware handlers.
|
List<ConfigBuilderEvent> |
getConfigBuilderEvents()
Get the list of Execution Events generated during the build of
the configuration.
|
FilterBypass |
getFilterBypass()
Get the
FilterBypass for this delivery configuration. |
List<?> |
getObjects(String selector)
Get a list of
Object s from the ResourceConfig s specified by the selector. |
Map<String,List<ResourceConfig>> |
getResourceConfigs()
Get the
ResourceConfig map for the target execution context. |
List<ResourceConfig> |
getResourceConfigs(String selector)
Get the list of
ResourceConfig s for the specified selector definition. |
boolean |
isDefaultSerializationOn()
Is default serialization on..
|
Filter |
newFilter(ExecutionContext executionContext)
Get a new stream filter for the content delivery configuration.
|
void |
sort()
|
static final String SMOOKS_VISITORS_SORT
produce
and consume
.
Default value "true".List<ResourceConfig> getResourceConfigs(String selector)
ResourceConfig
s for the specified selector definition.
This list will be preselected and preordered
for the target execution context.selector
- Configuration selector
. This
parameter is treated case incensitively.ResourceConfig
instances, or null if no ResourceConfig
s are
defined under that selector (for the device).getObjects(String)
Map<String,List<ResourceConfig>> getResourceConfigs()
ResourceConfig
map for the target execution context.
This Map will be preordered
for the target execution context.ResourceConfig
map for the target execution context, keyed by the configuration
selector
, with each value being a
List
of preordered ResourceConfig
instances.List<?> getObjects(String selector)
Object
s from the ResourceConfig
s specified by the selector.
Gets the ResourceConfig
s specified for the selector and attempts to instanciate
a Java class instance from the resource specified by each of the ResourceConfig
s.
Implementations should use Registry.lookup(Object)
} to
construct each object.selector
- selector attribute value from the .cdrl file in the .cdrar. This
parameter is treated case incensitively.Registry.lookup(Object)
,
getResourceConfigs(String)
Filter newFilter(ExecutionContext executionContext)
executionContext
- Execution context.List<ConfigBuilderEvent> getConfigBuilderEvents()
boolean isDefaultSerializationOn()
void sort() throws SmooksConfigException
SmooksConfigException
- Sort failure.SMOOKS_VISITORS_SORT
void addToExecutionLifecycleSets() throws SmooksConfigException
SmooksConfigException
- Error resolving the handlers interested
in the Execution lifecycle.void executeHandlerInit(ExecutionContext executionContext)
executionContext
- The execution context.void executeHandlerCleanup(ExecutionContext executionContext)
executionContext
- The execution context.FilterBypass getFilterBypass()
FilterBypass
for this delivery configuration.FilterBypass
for this delivery configuration, or
null if non configured.Copyright © 2023. All rights reserved.


1: Kernel
./linux-exploit-suggester.sh
2: Daemons
dpkg -l
3: Password Mining (Memory)
Potential for insecure protocols to have passwords in /proc filesystem or other
4: Sudo
sudo -l


Vi söker ny generalsekreterare
Text Susanne Mortazavi
Publicerad 19 September 2014
Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
Svenska Läkare Utan Gränser är en av landets största ideella organisationer. Vi är en stark röst i humanitära frågor och samlade förra året in 418 miljoner kronor. Nu söker vi en stark och inspirerande generalsekreterare som kan leda organisationen in i framtiden och vill göra sitt yttersta för att bidra till Läkare Utan Gränsers globala insatser.
GENERAL DIRECTOR, MSF SWEDEN
Läkare Utan Gränser/Médecins Sans Frontières (MSF) is an international medical humanitarian organisation determined to bring quality medical care to people in crises around the world, when and where they need it. Our fundamental principles are neutrality, impartiality, independence and medical ethics. In Sweden we raise money, recruit people and speak out to contribute to saving lives and alleviating suffering in the world. All our work is governed by a volunteer spirit.
We are looking for an inspirational General Director to lead Läkare Utan Gränser in Sweden into the future. The Swedish organisation has been very successful the last number of years. The role is truly challenging and highly visible in the Swedish society, in the MSF movement as well as in the wider external environment. You will need to be a strong leader capable of managing the effects of extensive growth. You have the ability to generate high performance in others. You have the confidence to operate within a complex and changeable international environment. And you have the tenacity to create the drive to do as much as possible to aid the cause of the MSF movement.
The General Director is responsible for the overall management of MSF Sweden and the achievement of its organisational goals and objectives.
Your position and responsibilities
Develop strategic priorities and goals of Läkare Utan Gränser, Sweden
Collaborate and network with the international MSF movement
Participate in key planning and evaluation processes of Läkare Utan Gränser, Sweden
Advocate and raise awareness of humanitarian issues within the Swedish society
Inspire, lead, coach and evaluate the teams of Läkare Utan Gränser, Sweden
Lead the Managenment Team of Läkare Utan Gränser in Sweden
Report to the Board of Directors of Läkare Utan Gränser, Sweden
Your profile
Extensive experience in the international humanitarian sector
A strong commitment to humanitarian ideas and principles
Strong leadership capacity, team approach to problem solving and diplomatic skills
A minimum of three years’ experience in a similar management position
Demonstrated analytical and planning abilities in complex settings
Excellent communication and networking skills
Fluency in spoken and written English and preferably fluency in spoken and written Swedish, French is an asset
Ability to work erratic hours and travelling
Our offer
A challenging position in an international environment
This is a unique opportunity to lead an organisation that makes a real difference, working alongside inspirational colleagues in an amazing organisation. It won’t always be easy, but the personal rewards are immense.
To apply
If you have any questions, please don’t hesitate to contact Alumni, our partner in this recruitment; +46 (0) 8 796 17 00. Please apply by sending your application (CV and cover letter) by October 12, 2014 to<EMAIL_ADDRESS>(marking it “8935”).
Aktuellt om
Läkemedelsresistent tuberkulos är en global folkhälsokris
Rapporter och forskning
10 November 2014
Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.
Leenden, tålamod och sår i själen
Nyheter från fältet
12 June 2014
Läkare Utan Gränser har arbetat i lägeren för syriska flyktingar sedan 2013. Där har Charlotte Yence startat program för psykisk hälsa.
Tre koppar starkt, nymalt kaffe
Nyheter från fältet
13 March 2014
Johanna Kuhlin arbetar sedan mitten av november i tre flyktingläger med ca 29 000 invånare i västra Etiopien. Här berättar hon själv om hur det är.
Just nu arbetar vi med


Basically, Streams 🌊
So something (relatively) new and fun in browsers, are these two APIs: ReadableStream and WritableStream. 😮
What they allow you to do, is basically send and receive chunks of things progressively (that then combine into the final large thing), as opposed to moving around entire things.
Below is a living example of streaming vs. conventional transfer:
I am a bunch of text who lives here, but I'm bored of living here so I want to move trololololololol.
Pretty cool, huh?
So basically, what's happening is this:
- Someone clicks "Move me!"
- We create a ReadableStream, whose source is the textContent of the left column
- The stream removes and queues each letter, one by one, every few milliseconds
- This stream is piped into a `receiverStream`, that writes it to the right column.
All of this is visible in the source code (index.js). There's more cool things in the examples folder. Go, explore, have fun! Woooo!
More Examples 🎉
Further Reading: WhatWG Spec


Design using Boostrap and CSS
Welcome to repository, you find CSS files and Boostrap integrated, i use banner, keyframes and styles for give special styles to the web page
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
you find all in PHDEVS
ReactJs/Angular
Python/Nodejs
MERN/MEAN


Honda is the world’s largest manufacturer of two Wheelers , Recognized the world over as the symbol of Honda two wheelers , the ‘Wings’ arrived in India as Honda Motorcycle and Scooter India Pvt. Ltd.
Suzuki is a Japanese manufacturer company which is well known for its reliable, strong and powerful motorcycles. The core philosophy of SUZUKI is to provide "VALUE-PACKED PRODUCTS".
TVS Motor Company is the third largest two-wheeler manufacturer in India and one among the top ten in the world.The company has a production capacity of 3 million 2 wheelers & 1.2 Lakh 3 wheelers a year.
India Yamaha Motor Private Limited (IYM) is an Indian subsidiary of Yamaha Motor Company, formed in 2008 as a joint venture with Mitsui.Bootsnipp - http://bootsnipp.com.
Its look like you are new here. To get a perfect personality match fill details


TaskTracker is a simple application that can be used to help store and report the hours that you spend on various tasks throughout the day. I wrote it when I started freelancing as I needed to track my hours carefully for invoicing. Even when I returned to permanent employment for a few years I continued to use it as I found it useful to know how much time was spent on development, support, documentation etc.
The application revolves around the notion of a session, which is the time spent on a single task.  There is no limit to the number of sessions you can create throughout the day, and a session can span multiple days. As well as associating a short name for the tasks, you can also specify the name of the location, which is useful if you work at multiple sites.
At some point you will want to know how your time has been spent, and to achieve this you can create a report. Most reports are focused around common periods such as a week or month to aid filling in timesheets or invoices. There is also a Task centric report for tracking overall time on each task.
My use has varied over the years, mostly depending on the level of reporting requested by my clients. In some cases I have only been required to submit a simple timesheet on a monthly basis with the number of hours per day. Other times I have been required to present a weekly timesheet, with a breakdown of how much time has been spent on individual tasks.
The main view of TaskTracker has 3 panels. The top one shows the current date & time, the bottom left shows the details of the current session, and the right one is a quick summary of the hours to date over the common reporting periods. The toolbar contains buttons to access the most common features and is grouped according to the menu structure - File, Session, Report and Help.
The following table shows the menu tree and provides links to the relevant sections.
| File | Session | Report | Prune | Tools | Help |
|---|---|---|---|---|---|
| Export | Clock In | Window | Sessions | Options | Contents |
| Import | Switch Tasks | Clipboard | Tasks | About | |
| Exit | Clock Out | Locations | |||
| Edit | File |
The main metaphor (hopefully obvious from the icon) used in TaskTracker is that of punching (or clocking) in and out at the ends of a shift. You may in fact work exactly in this way, but it also caters for people who either wish to classify their tasks in finer detail, work on very short tasks, or spend their day switching between different tasks. There is no limit to the number of sessions that can be associated with a day.
To clock in and out you use the "Session | Clock In..." and "Session | Clock Out..." menu commands, or use the toolbar buttons, or the shortcut keys Ctrl+I and Ctrl+O. These will present you with a dialog into which you can enter the details of the session. A session consists of a start and end date and time, a name for the task and a name for the location where it was performed. The dialog defaults to using the current date & time, and previously entered task name and location. If required you can clock in or out either in the past (if you forgot) or in the future. TaskTracker also maintains a list of all the task and location names you enter so you can save typing by picking a previous one using the combo boxes.
If you find yourself switching tasks quite frequently you can save time by using the "Session | Switch Tasks..." command (Ctrl+S) which only requires you to enter the task completed and the task about to be started and assumes that the dates and times should be adjacent.
You may find that over time you are either not around to enter your task details, or are that you need to adjust the timings, names or locations of some sessions. This can be done with the "Sessions | Edit..." command (Ctrl+E). This displays a dialog with all the sessions entered (except for the current one) ordered by the start date.
Through this dialog you can add, edit and remove whole sessions. There is also a button to allow you to rename a task (this changes the name of the task on all sessions that match).
As you can probably see from the screen shots, task names are only expected to be very short. They are only meant to be a quick reminder, so for example I would use task names like "TaskTracker Helptext" or "Bug #12345" rather than enter a longer description because that tends to go into other documentation. However if that is a feature that would be useful to many others then I will certainly consider putting it in.
There is nothing to stop you creating empty sessions, i.e. a session that begins and ends at the same point in time. This can be useful for adding sessions that you do not want to contribute towards the time worked, such as a holiday. A holiday doesn't count towards my hours worked, but I still want to have an entry on that day so that it's accounted for. It also means I can easily see how many days holiday I have taken.
At some point you are probably going to want to generate a summary of the hours that you have spent, either during the week, or on some tasks. Task Tracker allows you to create a few simple reports based either around a period in time or the tasks you have entered, and you can display this on screen (Ctrl+W), print it (Ctrl+P) or send it to the clipboard (Ctrl+C) or a file (Ctrl+F) using the various report commands under the "Report" sub menu.
All the dialogs for creating a report have the same basic layout. There are a number of options specific to the destination (e.g. filename or printer) and then some common "Parameters" that define the data that will be included.
The first of these is the Grouping. This defines how the session data
will be aggregated. If you are completing a timesheet you would probably use the
By week or By month option as you would want to group the sessions
into weeks or months, with an appropriate total. You may be required to list what
tasks you have been doing and how long you spent on each one, in which case the
By task report will sum according to each task name.
The grouping just defines the overall shape of the report, whereas the Period
narrows the range of dates down to just the set you are interested in; with some
pre-defined options already available, such as This Week and Last
Month.
Under the period, there are a few additional options that allow you to control
how much detail is generated. If you need the finest level of detail, check
Report session details as this will produce one line per session with
the start and end dates & times and both the task name and location included.
By default the report will not include days (or tasks) for which there is no
entry, e.g. Saturdays and Sundays, by you can force them to be included by
checking Report empty groups. Finally, if you are generating a report that
covers more than one grouping and you need a final total then tick the
Report overall total box.
There is one additional setting at the bottom left which is ticked by default
as it assumes that these are the default settings. This is probably what you want
most of the time, but occasionally you may want to generate an ad-hoc report and
don't want any defaults changed, in which case you should untick it.
The data is stored in a custom format binary file. However you can still export data (either as a means of backing it up or because you want to use the data externally in a tool like Excel) by using the "File | Export..." command (Ctrl+X). Just enter the filename and select the date range (which defaults to the entire database) and it will produce a CSV (Comma Separated Values) format file, with 4 fields per row - the start and end dates & times (in ISO format), the task name and the location name.
Alternatively you may wish to import data from another source (or restore data from a backup). This is done by using the "File | Import..." command (Ctrl+M). You specify the name of a CSV file, in the same format as TaskTracker exports, and choose whether you want to replace the entire database with what you are importing (e.g. a full restore) or merge the data into the database - with the ability to include or discard duplicate sessions.
TaskTracker keeps track of the name of every task and location that you use. This can mean that the task and location combo boxes eventually become heavily populated. You are also responsible for deleting sessions that you are no longer interested in. The "Prune" menu contains commands to allow you to trim the excess data.
The first is "Prune | Sessions..." which is a faster way to remove multiple contiguous sessions than selecting them one-by-one in the "Edit | Sessions..." dialog and pressing Delete. You can either delete the entire database, or remove a subset by specifying a date range.
The other two pruning commands "Prune | Tasks..." and "Prune | Locations" both behave the same way. They present you with a list of items that are no longer used (i.e. there are no sessions that have that task name or location), you can select one or more items and press OK to delete them.
There are a number of configuration settings for TaskTracker that can be changed through the "Tools | Options..." dialog.
The first setting "Minimise to system tray" changes TaskTracker so that it does not appear in the taskbar when minimised. It instead appears in the system tray area and provides a menu (via a right-click) for clocking in & out etc. I find this less intrusive that starting and stopping TaskTracker each time I switch tasks.
The "Warn if sessions overlap" checkbox controls whether TaskTracker will warn you (with a message box) if you try and enter a session that overlaps another. This can happen quite easily if you are manually adjusting sessions in the "Edit" dialog.
The "Session length format" setting determines how the length of sessions is displayed, both on screen and in a report. By default it's set to "Hours & Minutes", so an hour and a half would appear "1 h 30 m". Alternatively you can select "Hours Only" which displays them as a decimal, e.g. "1.5 h"
The final setting is used to describe the preferred order of the week. Some clients treat a working week from Monday to Sunday and others from Saturday to Friday. Changing this allows me to align my report with their practice.
This application is freeware - you get what you pay for, nothing more, nothing less.
The full source code (C++) is available from my web site listed below.
Rusty Biggs for testing it in a non GMT time zone.
The "Help | About" dialog also contains my contact details. Please check the web site for updates.
Email<EMAIL_ADDRESS>Web: www.cix.co.uk/~gort
Chris Oldwood
26th March 2007


We are proud we were able to deliver strong Core FFO growth despite the headwinds related to the pandemic, and we are well positioned for 2021. Average same-store occupancy in the 2nd half of 2020 reached all-time highs at 95.6%, resulting in double-digit rental rate pricing power, which will carry into 2021 and sets us up for another strong year of performance.
As we have continued to grow our Core FFO, our stock growth has continued as well. In 2020, our one-year total return to shareholders was 13.6% and our three-year return was 47.2%. Our 10-year return was 831.3%, the highest in the storage sector, and the third highest total shareholder return to any public REIT.
In 2020, we continued to evolve our balance sheet, increasing the size of our unencumbered pool, further laddering our debt maturities, and expanding our sources of capital. We completed a 10-year $425 million unsecured private placement debt transaction at an attractive coupon right in the middle of the recession, demonstrating our ability to access debt markets in uncertain times. We retired our $575 million convertible senior notes, and issued equity of approximately $109 million on our ATM.
These transactions led to a second investment grade issuer credit rating of Baa2 stable from Moody’s Investor Service in January 2021. Our Moody’s rating, in addition to our existing BBB rating with a stable outlook from S&P Global Ratings, provides us access to the public investment grade debt markets, another significant milestone for our already durable balance sheet. These advancements will further allow Extra Space to efficiently capitalize our growth for many years to come.
Comparison of 5 year cumulative total return*
Among Extra Space Storage Inc., the S&P 500 Index, and the FTSE Nareit Equity REITs Index


TwoDimBoussinesq¶
This gives a very brief introduction to how the Two-Dimensional Boussinesq equations are derive.
3D Model Equations¶
The three-dimensional Boussinesq equations is a simplified model from the Navier-Stokes equations. It is valid in the limit where the density variations are small compared to the mean density
This is a very good approximation for ocean dynamics and sometimes appropriate for the atmosphere. In this limit the density is approximated everywhere with a constant except in the buoyancy term. A very general way to write it, as used in the MITgcm is,
2D Model Equations: Vorticity Approach¶
There are many problems that can be studied in a two-dimensional context and are still interesting. To study stratified flows we assume that the fields can vary in one horizontal direction, say \(x\), and in the vertical, \(z\) We allow for motion in the \(y\) direction but no changes in that direction. In the 2D context the problem can be set up using the pressure method (cite something here) or using the vorticity method. Since TwoDimboussinesq focuses on the latter we focus on this approach.
Conservation of mass combined with the fact that no fields vary in the \(y\) direction, allow us to define a two-dimensional streamfunction which then determines the velocity field, \((u,w)\),
The momentum equations can be written in terms of the Jacobian,
To form the \(y\)-component of vorticity, \(\nabla^2 \psi = - \partial_z u + \partial_x w\) we compute the negative \(z\) derivative of the \(u\) eqn and add the \(z\) derivative of the \(w\) eqn,
Therefore the governing equations are,
If we assume that there is a background linear stratification denoted with \(\overline{b}(z) = N^2 z\) and decompose the buoyancy as
We substitute our decomposition into the equations and get,


 Usually, those who spent time on the front lines are getting little more than tap on shoulder, measly pension and rejection by society that considers them basketheads and losers; on the other hand, those who contributed to the war effort by doing nothing in safety of the rear are ones that are massing fame, fortune and political power. As this film shows, that sad truth is universal, and Vietnam War wasn't exception. John Steele (Martin Kove) and Lee Van Minh (Robert Kim) risk their lives at the end of war, only to see corrupt South Vietnamese general Kwan (Soon Teck-Or) taking CIA money and leaving them for die. Twelve years later, general Kwan is a respected businessman and unofficial leader of Vietnamese community in Los Angeles. John Steele, on the other hand, is an unemployed loser, who was thrown out of police five years ago. His war comrade and former partner Lee is still in the force, and unsuccessfully trying to connect Kwan with drug-smuggling operations. After Lee and his family get killed by Kwan's henchmen, Steele decides to go to war against Kwan to avenge his best friend's death.
For many critics STEELE JUSTICE was nothing more than routine and not very original LETHAL WEAPON rip-off, filled with all the clich‚s of 1980s action cinema. Here we have Vietnam veteran as a synonym for invincible one-man- army; businessmen that can't make money in any other way than by smuggling drugs; killing of partner/significant other as motive for revenge; and final showdown in a warehouse. What distinguishes this film from the rest of many similar B-products is huge army of capable actors like Ronny Cox, Bernie Casey and Joseph Campanella in supporting roles, together with Sarah Douglas, Sela Ward and Shannon Tweed that fill the babes quota. Unfortunately, the main lead, Martin Kove, looks like caricature of Rambo, same as Soon Teck-Or looks like a caricature of Fu Manchu. Although writer and director Robert Boris tries to take subject matter seriously, this movie occasionally slips into camp territory. Most obvious example is a sequence when rock music video, directed by Sela Ward's character, suddenly turns into gunfight between Steele and Asian assassins, led by Al Leong, Asian stuntman with long hair who made reputation by playing cannon fodder in such movies. All in all, although the viewer might get drowned in the sea of clich‚s and bad 1980s music, STEELE JUSTICE might serve him as mild guilty pleasure.
RATING: 5/10 (++)
Review written on October 19th 1998
Dragan Antulov a.k.a. Drax Fido: 2:381/100 E-Mail<EMAIL_ADDRESS><EMAIL_ADDRESS>The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Home / noticias / serviços de streaming
This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.
Last updated 3 mins ago
This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.
Last updated 3 mins ago
This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.
Last updated 3 mins ago


Introduction
R programming language is becoming highly popular these days and is heavily used in machine learning and data science projects. I have been learning data science from an online course at Coursera, and here are some of the concepts about R programming that I explored.
Here I assume, that the reader has basic knowledge of programming, since I do not dive deep in the grassroot level of explanation like variables, functions, etc. Basic knowledge of any programming language would be sufficient to get hold of following material. I have used command line R
for executing following scripts, however even R Studio can be used.
Setting up the workspace
getwd()
for getting present directory- Syntax for defining function
foo <- function() {
x <- rnorm(100) #rnorm generates a set of numbers from random normal distribution
mean(x)
}
- To pass a variable, simply define so in the paranthesis of
function()
- To load R scripts in console, use
source("script_name.R")
- To view loaded functions use,
ls()
- Call function for a range of values like this
foo(4:10)
. This will callfoo
for all values from 4 to 10. - Print something with,
print(x)
x <- 1:20
makes a vector
Data Types
- Atomic classes
- Character
- Numeric (double precision real numbers)
- Integer
- Complex
- Logical
- Most basic object is a vector, which containes objects of same class, except
list
- Vectors can be initialized with
vector()
- By default numbers are numeric. Can be explicitly declared as Integer by
1L
- Two specials,
Inf
andNaN
- Each objects have
attributes()
. Like names, dimensions, length, class, etc. c()
can be used to create vectors, its a way of concatenating. Similarlyvector()
can also be used.
x <- c(0.5,1)
x <- vector("numeric",length=10)
- Different objects can be mixed. Coersion will occur, so that eveything is of same class.
- Getting class of object using
class(x)
. Convert to numeric using,as.numeric(x)
. Same for character, etc. - Converting character to numeric gives ‘NA’
x <- list(1,"a", TRUE)
creates a list.- Matrices are special types of vectors with attribute dimension.
m <- matrix(nrow=2, ncol=2)
dim(m)
returns num of [rows , columns]- Matrix are cconstructed column wise ‘m <- (1:6, nrow = 2, ncol = 3)’
- Creating matrices from vectors.
m <- 1:10
dim(m) <- c(2,5)
cbind(a,b)
will bind vectors a and b column wise. Similarlyrbind()
- Factors can be used to represent categorical data, can be ordered or unordered. Treated specially using modelling functions like
glm()
orlm()
- Factors can be thought of as integer vector with levels.
- A NaN value is also an NA, but converse not true.
is.na()
andis.nan()
can be used. Note that NaN is mathematically not defined and NA is pretty much everything else.- Data Frames, used to store tabular data.
- Each element is a column of same length, but can be different type of objects
row.names
is an attributeread.table()
orread.csv()
function creates a dataframe- can be converted to
data.matrix()
. Coersion may occur ncol()
andnrow()
can be used for data frames
- R Objects can also have
names()
attribute.
x <- 1:3
names(x) <- c("foo","bar","nor")
# Also list elements can have names
x <- list(a=1,b=2,c=3)
# Matrix elements can also have names, called dimnames()
- http://swirlstats.com <- Learn R in R :p
(Debug tip -
sudo apt-get install libcurl4-openssl-dev libssl-dev
before installingswirl
)- Download the learning code in swirl and get going.
Reading and Writing data into R
read.table
andread.csv
gives a datafrom from a file.(opp iswrite.table
)readLines
gives text as a character vector (write.lines
)source
will read R code. (Opposite ofdump
)- Some other functions are
dget
,dput
,load
,save
,unserialize
.
data <- read.table("foo.txt")
# Each line with # will be skipped
- Dataset must be smaller than the RAM Size, else we need to optimize it. Also set
nrows
. See help page for hints. (GenerallycolClasses
fasterns the process. Seesapply()
function) - Calulate the size of your database. Rough estimate (8bytes/numeric value)
dput
saves the metadata. It writes R code which regenerates the data.
Interface to Outside world
- file, gzfile, bzfile, url
- Generally connections are handled implicitly.
- For reading url.
con <- url("http://eunotech.com")
x <- readLines(con)
head(x) #For printing few lines
Some more basics
- Two types, numeric indexed and logical indexed
- ’[’ returns objects of same class. Can select more than one elements (one exception -> matrix, which behaviour can be turned off by
x[1,2,drop = False]
) - ’[[’ returns an element may not be of same class
- ’$’ used to extract elements by name (from list or dataframe)
- R is ‘1’ indexed (not zero)
- logical subnetting can be done
x[x > "a"]
. Oru <- x > "a"
returns a boolean vector. x$foo
returns a value of an element that containsfoo
from list xx[c(1,3)]
returns 1st and 3rd valued from x- Partial matching can be allowed using
x[["a", exact = False]]
. Works with $ also. x[[name]]
, here name can be the index existing, or a varible pointing to an index of x. Whilex$name
will work only is x containes a key ‘name’.x[c(1,3)]
andx[[1]][[3]]
means same thing -> Nested access.- In matrix subnetting,
x[1:]
is valid, which means 1st row. - To remove missing values
bad <- is.na(x)
x[!bad] # Returns a vector with only elements that has values
good <- complete.cases(x,y) # returns a boolean vector which is true when both x and y element are present (not NA) --- Also can be used in dataframes
x+y
arithmatically adds vectors element-wise. Similarly,x > 2
can be used to get a boolean vector and so on.- For matrix multipication
x %*% y
must be used. Sincex * y
gives element-wise multiplication. - Use vectorized operations whenever possible to make the code simpler and optimized.
Control Structures
Generally used in R script, and not in interactive sessions
- if condition
if(<condition>){
}
else if {
} else {
}
- Also if structure can be used in this way
y <- if(x>3){
10
} else {
0
}
else
is optional- For loops can be defined as follows:
for(i in 1:10){
print(i)
}
seq_along()
function can be used to generate a vector that containes sequence based on the varible passed to it.- If only single line, you can omit curly braces and write everything in single line
- While loop can be used as follows
count <- 0
while(count < 10 && <condition2>){
print(count)
count <- count + 1
}
repeat
initiates an infinite loop. can be exited usingbreak
.
repeat {
if (){
break
# Break on some condition
}
}
next
can be used to skip and iterations. [likecontinue
]return
signals that a function should exit and return the given value.
Functions
- Write functions in files and not in interactive shell
add2 <- function(x,y) {
x + y
}
# can be called with add2(3,5)
- Here is how to take a vector as a paramater
above10 <- function(x, n=10){ #default values can be used
use <- x > n # Returns a logical vector
x[use] # Automatically last values is returned
}
- Have a look at this function
column_mean <- function(y, removeNA = TRUE){
nc <- ncol(y)
means <- numeric(nc) # Initilization of vector (default zeros)
for (i in 1:nc){
means[i] <- mean(y[,i], na.rm = removeNA) # Mean function takes na.rm arg
}
means # this will get returned
}
- Functions in R ae “first class objects”. They are R objects of class “function”
- Functions can be passed as arguments inside other function, and can be nested also.
- The arguments passed are known as ‘formal arguments’. Can be seen with
formals()
- Arguments can be named
sd(x <- mydata)
andsd(mydata)
are same. args(lm)
gives arguments taken by functionlm()
- Mixing named arguments with un-named can be done. Whatever are named, will be removed and rest are matched in the order of their definition.
- order of operationd for arguments
- Check for exact match
- Check for partial match
- Check for positional match
- Null is
NULL
, which means Nothing. Generally used for default value of formal arguments. - Arguments to functions are evaluated lazily.
f <- function(a,b){
a^2
}
f(2)
# Here argument b is never actually evaluated. Argument a is posinally matched and hence function will work.
- Special argument
...
, can be used when extending another function, when you don’t want to copy the entire argument list of the original function
myplot <- function(x,y,type = "l", ...){
plot(x, y, type=type, ...) # All other arguments are passed as it is
}
# Also generic functions use ... so that extra arguments can be passed to methods.
...
is necessary when the number of arguments passed to the function cannot be known in advance. One example that uses isargs()
- Note, arguments that appear after
...
must be explicitly named, no partial matching allowed
Scoping Rules
- You can create a function with name that already exists. For instance
lm <- function(x) { x + 1 }
- See
search()
. First.GlobalEnv
is checked for existing functions in there. Then it looks in the other packages. - Hence the order of the packages on the search list matters.
- Note that R has separate namespaces for functions and non-functions so it’s possible to have an object named c and a function named c.
- R use lexical scoping or static scoping. An alternative has dynamic scoping.
- Free variables are those used inside a function but not passed
- The values of free variables are searched for in the environment in which the function was defined. AN environment is a collection of (symbol, value) pairs. There is heirarchy of such environments.
- A function + an environment = a closure of function closure
- First the value of a symbol is searched in current environment, if not found, goes to parent env and so on.
- Generally global env is parent of all, for package the namespace is the parent of all.
- In R, functions can be inside other functions. Hence, the env is of other function.
make.power <- function(n){
pow <- function(x){
x^n
}
pow
}
cube <- make.power(3) # Returns a function that gives a cube
square <- make.power(2)
cube(3) # Gives 27
square(2) $ Gives 4
- See this example, value of f(3) will be different with lexical and dynamic scoping
y <- 10
f <- function(x){
y <- 2
y^2 + g(x)
}
g <- function(x){
x*y # this y will be 10 in lexical scoping (since value of y in the env in which g is defined is seen, but in dynamic scoping value of y will be 2 since the value in the 'calling env' is so.
# Calling env is also known as parent frame
}
# Note this happens only if function is called inside another function. If everything is defined and called globally, then it behaves like dynamic scoping. It looks like so.
- Languages that support lexical scoping.
- Scheme
- Perl
- Python
- Common Lisp (all language converges to List)
- Few optmization routins in R are,
optim
,nlm
,optimize
, takes a function and tries to find maximum or minimum.
Coding Standards
- Use text editors / text files
- Indent code (4 spaces)
- Limit the width of your code (80 columns?)
- Limit the length of functions
Dates and Times in R
- Dates are represented by the
Date
class. - Times are represented by POSIXct or the POSIXlt class
- Dates are stored internally as the number of days since 1970-01-01. Time, in seconds from the same date.
unclass(x)
is used to remove a class from an object xas.Date("1970-01-01")
can be used.- POSIX is a family of standards for representing data.
- Check
?strptime
for details. You can do operations on date time objects.
x <- Sys.time()
print(x)
p <- as.POSIXlt(x)
names(unclass(p))
## Will five sec, min, hour, mday, etc.
p$sec
Loop functions
lapply
: loop over a list and evaluate a function on each elementsapply
: also simplifies- Others are,
apply
,tapply
,mapply
, alsosplit
I hope that was helpful. Comment below for hugs and bugs.
comments powered by Disqus