-
Notifications
You must be signed in to change notification settings - Fork 44
/
default_response.json
16 lines (16 loc) · 26 KB
/
default_response.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"files": {
"postgis-open-cloud": {
"type": "text/html",
"content": " <section>\n <section>\n <h1>PostGIS</h1>\n <h2>in the Open Cloud</h2>\n <p>writing portable open source mapping applications</p>\n <p>with</p>\n <h2>Leaflet</h2>\n <p>and</p>\n <h2>OpenShift</h2>\n <br/>\n <p><a href='http://bit.ly/1tpBqGA'>bit.ly/1tpBqGA</a></p>\n <p><a href='http://ryanjarvinen.com/presentations/postgis-open-cloud'>http://ryanjarvinen.com/presentations/postgis-open-cloud</a></p>\n </section>\n <section>\n <div>presented by</div>\n <br/>\n <div><a href='http://ryanjarvinen.com/'>ryan jarvinen</a> / <a href='http://twitter.com/ryanj/'>@ryanj</a></div>\n </section>\n <section data-state=\"blackout\">\n <div>\n <div>Open Source Evangelist</div>\n <div>at</div>\n <div>Red Hat</div>\n <br/>\n <div>ryanj@redhat.com</div>\n </div>\n </section>\n </section>\n <section>\n <section>\n <h2>Agenda</h2>\n <ol>\n <li class='fragment'><a href='#/open-cloud'>Open Cloud Overview</a></li>\n <li class='fragment'><a href='#/frameworks'>Select a language and a lightweight web framwork</a></li>\n <li class='fragment'><a href='#/leaflet'>Add Leaflet for client-side map interactions</a></li>\n <li class='fragment'><a href='#/api'>Build a simple API</a></li>\n <li class='fragment'><a href='#/pg'>Install and configure PostgreSQL, PostGIS</a></li>\n <li class='fragment'><a href='#/env-vars'>Use environment variables to keep your code clean</a></li>\n <li class='fragment'><a href='#/action-hooks'>Bootstrap your DB</a></li>\n <li class='fragment'><a href='#/launch'>Launch your instant mapping solution</a></li>\n <li class='fragment'><a href='#/tune'>Learn about tuning PG on OpenShift</a></li>\n </ol>\n </section>\n </section>\n <section>\n <section id='open-cloud'>\n <p style='font-size:larger;'><b>the Cloud</b></p>\n <p class='fragment roll-in'><i>\"what is it made of?\"</i></p>\n </section>\n <section>\n <p style='font-size:larger;'>The cloud is:</p>\n <ul>\n <li class='fragment roll-in'>hot air?</li>\n <li class='fragment roll-in'>a series of tubes?</li>\n <li class='fragment roll-in'>mostly cat photos<span class=\"fragment roll-in\">✓</span></li>\n </ul>\n </section>\n <section>\n <h1>Infrastructure</h1>\n <h2 class='fragment'>as a service</h2>\n <p class='fragment' style='text-align:left;'><i>inputs:</i></p>\n <ul>\n <li class='fragment roll-in'><b>Hardware:</b> racks, fiber, routers, storage, compute</li>\n <li class='fragment roll-in'><b>Software:</b> OpenStack, Eucalyptus, CloudStack, SaltStack</li>\n </ul>\n <br/>\n <br/>\n <p class='fragment' style='text-align:left;'><i>Result:</i></p>\n <ul>\n <li class='fragment'>Easy on-demand Linux environments</li>\n </ul>\n </section>\n <section>\n <h1>Platform</h1>\n <h2 class='fragment'>as a service</h2>\n <p class='fragment' style='text-align:left;'><i>inputs:</i></p>\n <ul>\n <li class='fragment roll-in'><b>Hardware:</b> IaaS</li>\n <li class='fragment roll-in'><b>Software:</b> SELinux, CGroups, Docker, HAProxy, ssh, git</li>\n </ul>\n <br/>\n <br/>\n <p class='fragment' style='text-align:left;'><i>Result:</i></p>\n <ul>\n <li class='fragment'>Horizontally scalable application architectures on-demand</li>\n </ul>\n </section>\n <section>\n <p>Providing standards-based, open source workflows that answer the question of:</p>\n <br/>\n <div class='fragment' style='font-style: italic;font-weight:bold;background:#666;padding:2%;'>\n <p class='fragment grow' style='padding-bottom:4%'>\"How do I</p>\n <h1 style='display:inline-block;padding-bottom:6%;' class=\"fragment grow\">Build,</h1>\n <h1 style='display:inline-block;padding-left:7%;padding-bottom:6%;' class=\"fragment grow\">Host,</h1>\n <h1 style='display:inline-block;padding-left:7%;padding-bottom:6%;'>&</h1>\n <h1 style='display:inline-block;padding-left:7%;padding-bottom:2%;' class='fragment grow'>Scale</h1>\n <p class='fragment grow'>my solutions</p><p>on an</p>\n <p class='fragment grow'>Open Cloud?\"</p>\n </div>\n </section>\n <section>\n <p>Open platforms provide a peaceful environment for Developers AND Operations teams to work together in</h4>\n <img width=\"300\" height=\"303\" src=\"../shared/img/DarthLuke.png\">\n <ul>\n <li class=\"fragment\"><strong>Operations teams can help ensure system-wide stability and performance</strong></li>\n <li class=\"fragment\"><strong>Developers can quickly provision environments without waiting</strong></li>\n <li class=\"fragment\"><strong>The discussion shifts towards establishing great policies for scaling in response to demand</strong></li>\n </ul>\n </section>\n <section data-state='alert'>\n <h2>Terminology (Red Hat)</h2>\n <ul>\n <li class=\"fragment\" style='padding-bottom:20px;'><strong>Broker – Management host, orchestration of Nodes</strong></li>\n <li class=\"fragment\" style='padding-bottom:20px;'><strong>Node – Compute host containing Gears</strong></li>\n <li class=\"fragment\" style='padding-bottom:20px;'><strong>Gear – Allocation of fixed memory, compute, and storage resources for running applications (SELinux, CGoups)</strong></li>\n <li class=\"fragment\"><strong>Cartridge – A technology, framework, or application: Python, Ruby, Javascript, PHP, Perl, Java/JEE, PG, MySQL, Jenkins, PHPMyAdmin, etc.</strong></li> \n </ul>\n </section>\n <section>\n <h3>An Open Cartridge format</h3>\n <a href='http://openshift.github.io/documentation/oo_cartridge_developers_guide.html'><img style='width:50%' class='fragment roll-in' alt='OpenShift Cartridge' src='../shared/img/Openshift-Cartridge.png'/></a>\n <p><a href='http://openshift.github.io/documentation/oo_cartridge_developers_guide.html'>cart developer's guide</a></p>\n </section>\n <section>\n <img src='../shared/img/akbar_seems_legit.jpg'/><br/>\n </section>\n <section>\n <h3>OpenShift Release Schedule</h3>\n <img src=\"../shared/img/Three_products.png\">\n </section>\n </section>\n <section>\n <section id='frameworks'>\n <h1>Frameworks</h1>\n <ul>\n <li class=\"fragment\">NodeJS / Restify: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/restify-base'>https://github.com/ryanj/restify-base</a></span>\n </li>\n <li class=\"fragment\">Python / Flask: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/flask-base'>https://github.com/ryanj/flask-base</a></span>\n </li>\n <li class=\"fragment\">PHP / Silex: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/silex-base'>https://github.com/ryanj/silex-base</a></span>\n </li>\n <li class=\"fragment\">Ruby / Sinatra: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/sinatra-base'>https://github.com/ryanj/sinatra-base</a></span>\n </li>\n </ul>\n </section>\n <section>\n <h3>Language-specific Dependencies</h3>\n <p>Automatic support for dependency resolution using standard packaging, native to each language:</p>\n <p><a href='https://github.com/openshift-quickstart/adopt-a-hydrant-openshift-quickstart/blob/master/Gemfile'>gems</a> (ruby), <a href='https://github.com/shekhargulati/todo-flask-openshift-quickstart/blob/master/setup.py'>eggs</a> (python), and <a href='https://github.com/ryanj/darkslides/blob/master/package.json'>npm modules</a> (node.js)</p>\n </section>\n <section>\n <h3>Language-specific DB bindings</h3>\n <p>For nodejs:</p>\n <pre><code contenteditable>npm install pg-query --save</code></pre>\n <p><a href=\"https://npmjs.org/~brianc\">brianc's</a> <a href=\"https://npmjs.org/package/pg-query\"><code>pg-query</code> module</a> makes working with PG exceedingly simple</p>\n <p>Just map your queries to their related callback functions.</p>\n </section>\n <section>\n <h3>Local development</h3>\n <p>Resolve dependencies:</p>\n <pre><code contenteditable>npm install</code></pre>\n <p>Fire up a local server:</p>\n <pre><code contenteditable>npm start</code></pre>\n </section>\n </section>\n <section>\n <section id='leaflet'>\n <h1>Leaflet</h1>\n </section>\n <section>\n <p>Include a link to Leaflet's css stylesheet and javascript code in your index.html file:</p>\n <pre><code contenteditable><link rel=\"stylesheet\" href=\"//cdn.leafletjs.com/leaflet-0.5.1/leaflet.css\" />\n<script src=\"//cdn.leafletjs.com/leaflet-0.5.1/leaflet.js\"></script></code></pre>\n </section>\n <section>\n <p>Initialize the map:</p>\n <pre><code contenteditable>var map = L.map('map').setView([37.8, -122.3], 10);\nvar markerLayerGroup = L.layerGroup().addTo(map);\nL.tileLayer('http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png', {\n maxZoom: 18,\n minZoom: 5,\n attribution: 'Map tiles by <a href=\"http://stamen.com\">Stamen Design</a>, under <a href=\"http://creativecommons.org/licenses/by/3.0\">CC BY 3.0</a>. Data by <a href=\"http://openstreetmap.org\">OpenStreetMap</a>, under <a href=\"http://creativecommons.org/licenses/by-sa/3.0\">CC BY SA</a>.'\n}).addTo(map);</code></pre>\n </section>\n <section>\n <p>Update the Map on load, drag, or zoom:</p>\n <pre><code contenteditable>function getPins(e){\n bounds = map.getBounds();\n url = \"parks/within?lat1=\" + bounds.getSouthWest().lat + \"&lon1=\" + bounds.getSouthWest().lng + \"&lat2=\" + bounds.getNorthEast().lat + \"&lon2=\" + bounds.getNorthEast().lng;\n $.get(url, pinTheMap, \"json\")\n}\nfunction pinTheMap(data){\n //clear the current pins\n map.removeLayer(markerLayerGroup);\n //add the new pins\n var markerArray = new Array(data.length)\n for (var i = 0; i < data.length; i++){\n park = data[i];\n markerArray[i] = L.marker([park.lat, park.lon]).bindPopup(park.name);\n }\n markerLayerGroup = L.layerGroup(markerArray).addTo(map);\n}\nmap.on('dragend', getPins);\nmap.on('zoomend', getPins);\nmap.whenReady(getPins);\n</code></pre>\n </section>\n </section>\n <section>\n <section id='api'>\n <h1>Building an API</h1>\n </section>\n <section>\n <pre><code contenteditable>var config = require('config'),\n restify = require('restify'),\n fs = require('fs'),\n db = require('./bin/db.js')\nvar app = restify.createServer()\napp.use(restify.queryParser())\napp.use(restify.CORS())\napp.use(restify.fullResponse())\n \n// Routes\napp.get('/parks/within', db.selectBox);\napp.get('/parks', db.selectAll);\n// Static assets\napp.get(/\\/(css|js|img)\\/?.*/, restify.serveStatic({directory: './static/'}));\napp.get('/', function (req, res, next)\n{\n var data = fs.readFileSync(__dirname + '/index.html');\n res.status(200);\n res.header('Content-Type', 'text/html');\n res.end(data.toString().replace(/host:port/g, req.header('Host')));\n});\n \napp.listen(config.port, config.ip, function () {\n console.log( \"Listening on \" + config.ip + \", port \" + config.port )\n});\n</code></pre>\n </section>\n </section>\n <section>\n <section id='pg'>\n <h1>PG Setup</h1>\n </section>\n <section>\n <h4>Adding Postgres to existing apps:</h4>\n <pre><code contenteditable>rhc cartridge add postgres-8.4</code></pre>\n <p>or</p>\n <pre><code contenteditable>rhc cartridge add postgres-9.2</code></pre>\n <br/>\n <p class=\"fragment roll-in\"><i>done!</i></p>\n <br/>\n <p class=\"fragment roll-in\"><a href='https://www.openshift.com/blogs/postgresql-92-comes-to-openshift'>blog post: PostgreSQL 9.2 Comes to OpenShift</a></p>\n </section>\n <section>\n <pre><code contenteditable>function select_box(req, res, next){\n //clean our input variables before forming our DB query:\n var query = req.query;\n var limit = (typeof(query.limit) !== \"undefined\") ? query.limit : 40;\n if(!(Number(query.lat1) \n && Number(query.lon1) \n && Number(query.lat2) \n && Number(query.lon2)\n && Number(limit)))\n {\n res.send(500, {http_status:400,error_msg: \"this endpoint requires two pair of lat, long coordinates: lat1 lon1 lat2 lon2\\na query 'limit' parameter can be optionally specified as well.\"});\n return console.error('could not connect to postgres', err);\n }\n pg('SELECT gid,name,ST_X(the_geom) as lon,ST_Y(the_geom) as lat FROM ' + table_name+ ' t WHERE ST_Intersects( ST_MakeEnvelope('+query.lon1+\", \"+query.lat1+\", \"+query.lon2+\", \"+query.lat2+\", 4326), t.the_geom) LIMIT \"+limit+';', function(err, rows, result){\n if(err) {\n res.send(500, {http_status:500,error_msg: err})\n return console.error('error running query', err);\n }\n res.send(rows);\n return rows;\n })\n};</code></pre>\n </section>\n </section>\n <section>\n <section id='env-vars'>\n <h1>Env Vars</h1>\n <h3>For writing Clean and Portable Code<h3>\n </section>\n <section>\n<pre><code contenteditable>rhc app show rss</code></pre>\n<p>Or, while connected over ssh:</p>\n<pre><code contenteditable>env | grep DB</code></pre>\n<pre><code contenteditable>OPENSHIFT_POSTGRESQL_DB_PASSWORD=lXcFVx4hIZgR\nOPENSHIFT_POSTGRESQL_DB_SOCKET=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/socket/\nOPENSHIFT_POSTGRESQL_DB_HOST=127.7.8.130\nOPENSHIFT_POSTGRESQL_DB_PID=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/pid/postgres.pid\nOPENSHIFT_POSTGRESQL_DB_USERNAME=adminpahue6e\nOPENSHIFT_POSTGRESQL_DB_URL=postgresql://adminpahue6e:lXcFVx4hIZgR@127.7.8.130:5432\nOPENSHIFT_POSTGRESQL_DB_PORT=5432\nOPENSHIFT_POSTGRESQL_DB_LOG_DIR=/var/lib/openshift/523672f7e0b8cd02d70003bc/postgresql/log/</code></pre>\n </section>\n <section>\n <p>Persist configuration details, <br/>while keeping your source clean:</p>\n <pre><code contenteditable>module.exports = {\n port: process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 3000,\n ip: process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1',\n pg_config: process.env.OPENSHIFT_POSTGRESQL_DB_URL || 'postgresql://127.0.0.1:5432',\n table_name: process.env.OPENSHIFT_APP_NAME || process.env.PG_MAP_TABLE_NAME || 'parks'\n}</code></pre>\n </section>\n <section>\n <h3>Environment Variables</h3>\n <p>Listing your custom env vars:</p>\n <pre><code contenteditable>cd myapp \nrhc env list</code></pre>\n <p>Setting a variable:</p>\n <pre><code contenteditable>rhc env set SECRET_TOKEN=\"a1fdacc3b1d14d6a92ed1219ed304d02529f535085262a90c39f072ef6de0ee9fe3a3d0194f02a2a8eb3\"</code></pre>\n <p>Help with configuration:</p>\n <pre><code contenteditable>rhc help env</code></pre>\n </section>\n <section>\n <p>Or, supply additional keys during the app creation process:</p>\n <pre><code contenteditable>rhc app create hydrant ruby-1.9 postgresql-8.4 --from=code=http://github.com/ryanj/adopt-a-hydrant.git --env SECRET_TOKEN=\"YOUR_SECRET_TOKEN\"</code></pre>\n <p><a href='http://hydrant-shifter.rhcloud.com/'>http://hydrant-shifter.rhcloud.com/</a></p>\n </section>\n <section>\n <h3>Advanced DB services mapping</h3>\n <p><a href='https://www.openshift.com/blogs/set-up-local-access-to-openshift-hosted-services-with-port-forwarding'>Using port-forwarding for local dev</a></p>\n <p><a href='https://www.openshift.com/blogs/cloud-connections-how-to-use-openshift-with-external-databases'>Or, connect to existing hosted PG services</a></p>\n </section>\n </section>\n <section>\n <section id='action-hooks'>\n <h2>Automate your DB Setup</h2>\n </section>\n <section>\n <h3>Action Hooks</h3>\n <ol>\n <li class=\"fragment roll-in\">enable postgis</li>\n <li class=\"fragment roll-in\">create your table schema</li>\n <li class=\"fragment roll-in\">add a geospatial index</li>\n <li class=\"fragment roll-in\">bootstrap your db</li>\n </ol>\n <br/>\n <br/>\n <p class=\"fragment roll-in\"><a href='https://github.com/ryanj/restify-postGIS/'>https://github.com/ryanj/restify-postGIS/tree/master/.openshift/action_hooks</a></p>\n </section>\n <section>\n <pre><code contenteditable>var config = require('config'),\n pg = require('pg-query')\nvar pg_config = config.pg_config,\n table_name = config.table_name;\npg.connectionParameters = pg_config + '/' +table_name;\nvar points = require('../parkcoord.json');\n \nfunction initDB(){\n pg('CREATE EXTENSION postgis;', createDBSchema);\n} \nfunction createDBSchema(err, rows, result) {\n if(err && err.code == \"ECONNREFUSED\"){\n return console.error(\"DB connection unavailable, see README notes for setup assistance\\n\", err);\n }\n var query = \"CREATE TABLE \"+table_name+\n \" ( gid serial NOT NULL, name character varying(240), the_geom geometry, CONSTRAINT \"+table_name+ \"_pkey PRIMARY KEY (gid), CONSTRAINT enforce_dims_geom CHECK (st_ndims(the_geom) = 2), CONSTRAINT enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL),CONSTRAINT enforce_srid_geom CHECK (st_srid(the_geom) = 4326) ) WITH ( OIDS=FALSE );\";\n pg(query, addSpatialIndex);\n};</code></pre>\n </section>\n <section>\n <pre><code contenteditable>function addSpatialIndex(err, rows, result) {\n pg(\"CREATE INDEX \"+table_name+\"_geom_gist ON \"+table_name+\" USING gist (the_geom);\", importMapPoints);\n}\nfunction importMapPoints(err, rows, result) {\n var query = \"Insert into \"+table_name+\" (name, the_geom) VALUES \" + points.map(mapPinSQL).join(\",\") + ';';\n pg(query, function(err, rows, result) {\n var response = 'Data import completed!';\n return response;\n });\n};\nfunction mapPinSQL(pin) {\n var query = ''; \n if(typeof(pin) == 'object'){\n query = \"('\" + pin.Name.replace(/'/g,\"''\") + \"', ST_GeomFromText('POINT(\" + pin.pos[0] +\" \"+ pin.pos[1] + \" )', 4326))\"; \n }\n return query;\n};</code></pre>\n </section>\n </section>\n <section>\n <section>\n <h1>Results!</h1>\n <ul>\n <li class=\"fragment\">NodeJS / restify: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/restify-postGIS'>https://github.com/ryanj/restify-postGIS</a></span>\n </li>\n <li class=\"fragment\">Python / Flask: <br/>\n <span class='fragment'><a href='https://github.com/ryanj/flask-postGIS'>https://github.com/ryanj/flask-postGIS</a></span>\n </li>\n <li class=\"fragment\">Java / Hibernate: <br/>\n <span class='fragment'><a href='https://github.com/thesteve0/parkshibernatespatial'>https://github.com/thesteve0/parkshibernatespatial</a></span>\n </li>\n </ul>\n </section>\n </section>\n <section>\n <section id='launch'>\n <h1>Launch Time!</h1>\n <p class='fragment'>Spin up a fresh app from the command line:</p>\n <pre class='fragment'><code contenteditable>rhc app create myapp cart1 cart2 --from-code=http://github.com/user/repo.git</code></pre>\n <p class='fragment'>For our nodejs example:</p>\n <pre class='fragment'><code contenteditable>rhc app create nodegis nodejs-0.10 postgres-9.2 --from-code=http://github.com/ryanj/restify-postGIS.git</code></pre>\n </section>\n <section>\n <p style='font-style:italic;'>Live Result</p>\n <p class='fragment'><a href='http://nodegis-shifter.rhcloud.com/'><img src='https://www.openshift.com/sites/default/files/Parks_preview.png' /><br/>nodegis-shifter.rhcloud.com/</a></p>\n </section>\n <section>\n <p><i>~ related content ~</i></p>\n <h3 class='fragment'>Web Workflows for Launching Apps</h3>\n <p class='fragment'><a href='https://www.openshift.com/blogs/customizing-openshifts-web-based-app-creation-workflow'>Customizing OpenShift's Web-based App Creation Workflow</a></p>\n <br/>\n <h3 class='fragment'>Open Source Ribbons for Launching Apps</h3>\n <p class='fragment'><a href='https://www.openshift.com/blogs/instant-hosting-of-open-source-projects-with-github-style-ribbons'>Instant Hosting of Open Source Projects with GitHub-style Ribbons</a></p>\n <br/>\n <h3 class='fragment'>Custom Domain Names and SSL</h3>\n <p class='fragment'><a href='https://www.openshift.com/blogs/domain-names-and-ssl-in-the-openshift-web-console'>Domain Names and SSL in the OpenShift Web Console</a></p>\n </section>\n </section>\n <section>\n <section id='tune'>\n <h1>Tuning PG</h1>\n <p class='fragment'>aka, where is my pg_hba.conf, and postgresql.conf?</p>\n </section>\n <section>\n <p>Some PG tuning notes were recently posted in the <a href='https://www.openshift.com/blogs/more-online-features-for-april-2014#pg-tuning'>OpenShift Online release announcement for April 2014</a></p>\n <pre><code contenteditable>OPENSHIFT_POSTGRESQL_SHARED_BUFFERS</code></pre>\n <pre><code contenteditable>OPENSHIFT_POSTGRESQL_MAX_CONNECTIONS</code></pre>\n <br/>\n <br/>\n <p>More general tuning advice: <a href='https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server'>https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server</a></p>\n </section>\n <section>\n<h3>Advanced configurations</h3>\n<ul>\n <li class='fragment'>Watch out for statistics collector issues in the pg-9.2 cart</li>\n</ul>\n<br/>\n<br/>\n<p class='fragment'>Take a look at the latest from CruchyData!</h3>\n<ul>\n <li class='fragment'><a href='https://github.com/crunchyds/openshift-postgres-cartridge'>9.3 cart is available</a></li>\n <li class='fragment'><a href='https://github.com/crunchyds/openshift-postgres-rls-cartridge'>9.4 devel with RLS patch</a></li>\n</ul>\n </section>\n <section>\n<h2>HA for PG by CrunchyData</h2>\n<p>See their release announcement for additional details: <a href='http://crunchydatasolutions.com/crunchy-latest/crunchy-announces-high-availability-postgresql-for-openshift-enterprise/'>crunchydatasolutions.com</a></p>\n<br/>\n<p>Including support for LB, geographic failover, Master-slave replication, and more!</p>\n </section>\n </section>\n <section>\n <section>\n <h2>Join the Community</h2>\n <ul>\n <li class=\"fragment\">We accept pull requests</li>\n <li class=\"fragment\">Help with anything from core, quickstarts, and cartridges, to small typo fixes in the command line tools </li>\n <li class=\"fragment\">PEPs for major feature enhancements</li>\n <li class=\"fragment\"><a href=\"https://github.com/openshift/origin-server/blob/master/CONTRIBUTING.md\" class=\"roll\">Contribution Guidelines</a></li>\n <li class=\"fragment\"><a href=\"https://trello.com/openshift\" class=\"roll\"><span data-title=\"Public Trello cards\">Public Trello cards</span></a></li>\n <li class=\"fragment\"><a href=\"https://tcms-openshift.rhcloud.com/plan/2/openshift-origin\" class=\"roll\"><span data-title=\"Public Test plans\">Public Test plans</span></a></li>\n <li class=\"fragment\"><a href=\"https://bugzilla.redhat.com/\" class=\"roll\"><span data-title=\"Public Bugzilla\">Public Bugzilla</span></a></li>\n <li class=\"fragment\"><a href=\"https://www.openshift.com/ideas\" class=\"roll\"><span data-title=\"Voting on Features\">Vote on Features</span></a></li>\n </ul> \n </section>\n <section>\n <h2>Origin.ly</h2>\n <p><a href='http://origin.ly/search?query=postgres'>The Origin.ly Appication and Cartridge index</a></p>\n </section>\n <section>\n <h2>OpenShift Core Roadmap:</h2>\n <ul>\n <li class=\"fragment\"><a href='http://www.projectatomic.io/'>Project Atomic</a></li>\n <li class=\"fragment\"><a href='https://www.openshift.com/blogs/technical-thoughts-on-openshift-and-docker'>Docker</a></li>\n <li class=\"fragment\"><a href='https://www.openshift.com/blogs/geard-the-intersection-of-paas-docker-and-project-atomic'>GearD</a></li>\n </ul>\n </section>\n <section>\n <p>Check out the upstream source: <br/><a href='http://openshift.github.com/'>OpenShift Origin</a></p>\n <br/>\n <p>Try our hosted solution (3 apps free): <br/><a href='https://openshift.redhat.com/app/account/new?web_user[promo_code]=PGCon2014'>OpenShift Online</a></p>\n <br/>\n <p>Request an evaluation for: <br/><a href='https://www.openshift.com/products/enterprise'>OpenShift Enterprise</a></p>\n </section>\n <section data-state=\"blackout\" id='bye'>\n <h1>Thank You!</h1>\n <p>See my post on this topic for more info: <a href='https://www.openshift.com/blogs/instant-mapping-applications-with-postgis-and-nodejs'>Instant Mapping Applications with PostGIS and Nodejs</a></p>\n <br/>\n <p>Link to these slides: <a href='http://bit.ly/1tpBqGA'>bit.ly/1tpBqGA</a></p>\n <br/>\n <p><b><i>See you next time!<br/> --ryanj</i></b></p>\n </section>\n </section>"
}
},
"description": "Writing portable postgreSQL-backed applications for the Open Cloud",
"owner": {
"login": "ryanj",
"avatar_url": "https://avatars.githubusercontent.com/u/17562?",
"gravatar_id": "ee8fbe2cda4f05ef6dea595ced8f6e1a",
"url": "https://api.github.com/users/ryanj",
"html_url": "https://github.com/ryanj"
}
}