├── .meteor ├── .gitignore ├── release ├── platforms ├── .finished-upgraders ├── .id ├── packages └── versions ├── packages.json ├── packages └── npm-container │ ├── .npm │ └── package │ │ ├── .gitignore │ │ ├── README │ │ └── npm-shrinkwrap.json │ ├── index.js │ └── package.js ├── lib └── neo4j.js ├── LICENSE ├── leaderboard.html ├── .gitignore ├── leaderboard.js ├── leaderboard.css └── readme.md /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.3 2 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /packages.json: -------------------------------------------------------------------------------- 1 | { 2 | "neo4j": "1.1.1" 3 | } -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /lib/neo4j.js: -------------------------------------------------------------------------------- 1 | /* Allow client query execution */ 2 | Meteor.neo4j.allowClientQuery = true; 3 | /* Custom URL to Neo4j should be here */ 4 | Meteor.neo4j.connectionURL = 'http://neo4j:1234@localhost:7474'; 5 | /* But deny all writing actions on client */ 6 | Meteor.neo4j.set.deny(Meteor.neo4j.rules.write); -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | 1wrj5x8mo7orr2rtoe0 8 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | meteor-platform 8 | ostrio:neo4jdriver 9 | ostrio:neo4jreactivity 10 | audit-argument-checks -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/README: -------------------------------------------------------------------------------- 1 | This directory and the files immediately inside it are automatically generated 2 | when you change this package's NPM dependencies. Commit the files in this 3 | directory (npm-shrinkwrap.json, .gitignore, and this README) to source control 4 | so that others run the same versions of sub-dependencies. 5 | 6 | You should NOT check in the node_modules directory that Meteor automatically 7 | creates; if you are using git, the .gitignore file tells git to ignore it. 8 | -------------------------------------------------------------------------------- /packages/npm-container/index.js: -------------------------------------------------------------------------------- 1 | Meteor.npmRequire = function(moduleName) { // 85 2 | var module = Npm.require(moduleName); // 86 3 | return module; // 87 4 | }; // 88 5 | // 89 6 | Meteor.require = function(moduleName) { // 90 7 | console.warn('Meteor.require is deprecated. Please use Meteor.npmRequire instead!'); // 91 8 | return Meteor.npmRequire(moduleName); // 92 9 | }; // 93 -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | audit-argument-checks@1.0.3 2 | autoupdate@1.2.1 3 | base64@1.0.3 4 | binary-heap@1.0.3 5 | blaze@2.1.2 6 | blaze-tools@1.0.3 7 | boilerplate-generator@1.0.3 8 | callback-hook@1.0.3 9 | check@1.0.5 10 | coffeescript@1.0.6 11 | ddp@1.1.0 12 | deps@1.0.7 13 | ejson@1.0.6 14 | fastclick@1.0.3 15 | geojson-utils@1.0.3 16 | html-tools@1.0.4 17 | htmljs@1.0.4 18 | http@1.1.0 19 | id-map@1.0.3 20 | jquery@1.11.3_2 21 | json@1.0.3 22 | launch-screen@1.0.2 23 | livedata@1.0.13 24 | logging@1.0.7 25 | meteor@1.1.6 26 | meteor-platform@1.2.2 27 | minifiers@1.1.5 28 | minimongo@1.0.8 29 | mobile-status-bar@1.0.3 30 | mongo@1.1.0 31 | observe-sequence@1.0.6 32 | ordered-dict@1.0.3 33 | ostrio:minimongo-extensions@1.0.1 34 | ostrio:neo4jdriver@0.2.15 35 | ostrio:neo4jreactivity@0.9.1 36 | random@1.0.3 37 | reactive-dict@1.1.0 38 | reactive-var@1.0.5 39 | reload@1.1.3 40 | retry@1.0.3 41 | routepolicy@1.0.5 42 | session@1.1.0 43 | sha@1.0.3 44 | spacebars@1.0.6 45 | spacebars-compiler@1.0.6 46 | templating@1.1.1 47 | tracker@1.0.7 48 | ui@1.0.6 49 | underscore@1.0.3 50 | url@1.0.4 51 | webapp@1.2.0 52 | webapp-hashing@1.0.3 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Veliov Group Inc., ostr.io, dr.dimitru, Dmitriy Aristarkhovich, Dmitry A. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /leaderboard.html: -------------------------------------------------------------------------------- 1 | 2 | Leaderboard 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |

Leaderboard

11 |
Select a scientist to give them points
12 | {{> leaderboard}} 13 |
14 | 15 | 16 | 35 | 36 | 43 | 44 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | .idea 19 | *.lock 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Dependency directory 25 | # Deployed apps should consider commenting this line out: 26 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 27 | node_modules 28 | npm-debug.log 29 | *.scssc 30 | .sass-cache/* 31 | *-ck.js 32 | providers_secret.js 33 | 34 | codekit-config.json 35 | 36 | config.rb 37 | 38 | deploy.sh 39 | 40 | .demeteorized 41 | 42 | dump/ 43 | dump/* 44 | 45 | settings.json 46 | settings.json.not-used 47 | 48 | scratch 49 | 50 | .deploy 51 | .deploy/* 52 | ### Meteor template 53 | .meteor/local 54 | .meteor/meteorite 55 | mup.json 56 | 57 | # OSX.gitignore 58 | # https://github.com/github/gitignore/blob/master/Global/OSX.gitignore 59 | .DS_Store 60 | .AppleDouble 61 | .LSOverride 62 | 63 | # Icon must end with two \r 64 | Icon 65 | 66 | 67 | # Thumbnails 68 | ._* 69 | 70 | # Files that might appear on external disk 71 | .Spotlight-V100 72 | .Trashes 73 | 74 | # Directories potentially created on remote AFP share 75 | .AppleDB 76 | .AppleDesktop 77 | Network Trash Folder 78 | Temporary Items 79 | .apdisk 80 | 81 | # cache files for sublime text 82 | *.tmlanguage.cache 83 | *.tmPreferences.cache 84 | *.stTheme.cache 85 | 86 | # workspace files are user-specific 87 | *.sublime-workspace 88 | 89 | # project files should be checked into the repository, unless a significant 90 | # proportion of contributors will probably not be using SublimeText 91 | *.sublime-project 92 | 93 | # sftp configuration file 94 | sftp-config.json -------------------------------------------------------------------------------- /packages/npm-container/package.js: -------------------------------------------------------------------------------- 1 | var path = Npm.require('path'); // 97 2 | var fs = Npm.require('fs'); // 98 3 | // 99 4 | Package.describe({ // 100 5 | summary: 'Contains all your npm dependencies', // 101 6 | version: '1.0.0', // 102 7 | name: 'npm-container' // 103 8 | }); // 104 9 | // 105 10 | var packagesJsonFile = path.resolve('./packages.json'); // 106 11 | try { // 107 12 | var fileContent = fs.readFileSync(packagesJsonFile); // 108 13 | var packages = JSON.parse(fileContent.toString()); // 109 14 | Npm.depends(packages); // 110 15 | } catch(ex) { // 111 16 | console.error('ERROR: packages.json parsing error [ ' + ex.message + ' ]'); // 112 17 | } // 113 18 | // 114 19 | Package.onUse(function(api) { // 115 20 | api.add_files(['index.js', '../../packages.json'], 'server'); // 116 21 | }); // 117 -------------------------------------------------------------------------------- /packages/npm-container/.npm/package/npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "neo4j": { 4 | "version": "1.1.1", 5 | "dependencies": { 6 | "http-status": { 7 | "version": "0.1.8" 8 | }, 9 | "request": { 10 | "version": "2.51.0", 11 | "dependencies": { 12 | "bl": { 13 | "version": "0.9.3", 14 | "dependencies": { 15 | "readable-stream": { 16 | "version": "1.0.33", 17 | "dependencies": { 18 | "core-util-is": { 19 | "version": "1.0.1" 20 | }, 21 | "isarray": { 22 | "version": "0.0.1" 23 | }, 24 | "string_decoder": { 25 | "version": "0.10.31" 26 | }, 27 | "inherits": { 28 | "version": "2.0.1" 29 | } 30 | } 31 | } 32 | } 33 | }, 34 | "caseless": { 35 | "version": "0.8.0" 36 | }, 37 | "forever-agent": { 38 | "version": "0.5.2" 39 | }, 40 | "form-data": { 41 | "version": "0.2.0", 42 | "dependencies": { 43 | "async": { 44 | "version": "0.9.0" 45 | }, 46 | "mime-types": { 47 | "version": "2.0.7", 48 | "dependencies": { 49 | "mime-db": { 50 | "version": "1.5.0" 51 | } 52 | } 53 | } 54 | } 55 | }, 56 | "json-stringify-safe": { 57 | "version": "5.0.0" 58 | }, 59 | "mime-types": { 60 | "version": "1.0.2" 61 | }, 62 | "node-uuid": { 63 | "version": "1.4.2" 64 | }, 65 | "qs": { 66 | "version": "2.3.3" 67 | }, 68 | "tunnel-agent": { 69 | "version": "0.4.0" 70 | }, 71 | "tough-cookie": { 72 | "version": "0.12.1", 73 | "dependencies": { 74 | "punycode": { 75 | "version": "1.3.2" 76 | } 77 | } 78 | }, 79 | "http-signature": { 80 | "version": "0.10.1", 81 | "dependencies": { 82 | "assert-plus": { 83 | "version": "0.1.5" 84 | }, 85 | "asn1": { 86 | "version": "0.1.11" 87 | }, 88 | "ctype": { 89 | "version": "0.5.3" 90 | } 91 | } 92 | }, 93 | "oauth-sign": { 94 | "version": "0.5.0" 95 | }, 96 | "hawk": { 97 | "version": "1.1.1", 98 | "dependencies": { 99 | "hoek": { 100 | "version": "0.9.1" 101 | }, 102 | "boom": { 103 | "version": "0.4.2" 104 | }, 105 | "cryptiles": { 106 | "version": "0.2.2" 107 | }, 108 | "sntp": { 109 | "version": "0.2.4" 110 | } 111 | } 112 | }, 113 | "aws-sign2": { 114 | "version": "0.5.0" 115 | }, 116 | "stringstream": { 117 | "version": "0.0.4" 118 | }, 119 | "combined-stream": { 120 | "version": "0.0.7", 121 | "dependencies": { 122 | "delayed-stream": { 123 | "version": "0.0.5" 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /leaderboard.js: -------------------------------------------------------------------------------- 1 | /*jshint strict:false */ 2 | /*global Meteor:false */ 3 | /*global Template:false */ 4 | /*global $:false */ 5 | /*global _:false */ 6 | /*global Session:false */ 7 | /*global Tracker:false */ 8 | /*global Random:false */ 9 | 10 | 11 | var Players = Meteor.neo4j.collection('players'); 12 | 13 | if (Meteor.isClient) { 14 | Session.setDefault('searchString', "."); 15 | 16 | Tracker.autorun(function(){ 17 | Players.subscribe('allPlayers', {search: Session.get('searchString')}, 'node'); 18 | }); 19 | 20 | Template.leaderboard.helpers({ 21 | players: function () { 22 | return Players.find({ 23 | 'metadata.labels': 'Player', 24 | name: { 25 | '$regex': Session.get('searchString')+'*.', 26 | '$options': 'i' 27 | } 28 | }, { 29 | sort:{ 30 | score: -1 31 | } 32 | }); 33 | }, 34 | selectedName: function () { 35 | var player = Players.findOne({_id: Session.get('selectedPlayer')}); 36 | if(player){ 37 | return player.name; 38 | } 39 | }, 40 | selectedPlayer: function(){ 41 | return Session.get('selectedPlayer'); 42 | } 43 | }); 44 | 45 | Template.leaderboard.events({ 46 | 'click .inc': function () { 47 | Players.update({ 48 | _id: Session.get('selectedPlayer') 49 | },{ 50 | '$inc': { 51 | score: 5 52 | } 53 | }); 54 | }, 55 | 'keyup #search': function(e) { 56 | searchString = e.currentTarget.value; 57 | if(searchString.length > 0){ 58 | Session.set('searchString', (searchString+'').replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&")); 59 | }else{ 60 | Session.set('searchString', "."); 61 | } 62 | } 63 | }); 64 | 65 | Template.addPlayer.events({ 66 | 'click #addPlayer': function () { 67 | if($('#newPlayerName').val()){ 68 | Players.insert({ 69 | name: $('#newPlayerName').val(), 70 | score: 0, 71 | __labels: ':Player' 72 | }); 73 | $('#newPlayerName').val(''); 74 | } 75 | } 76 | }); 77 | 78 | Template.player.helpers({ 79 | selected: function () { 80 | if(Session.equals('selectedPlayer', this._id)){ 81 | return 'selected'; 82 | } 83 | } 84 | }); 85 | 86 | Template.player.events({ 87 | 'click': function () { 88 | Session.set('selectedPlayer', this._id); 89 | }, 90 | 'click #removePlayer': function (e) { 91 | $(e.target).parent().remove(); 92 | Players.remove({_id: this._id}); 93 | Session.set('selectedPlayer', false); 94 | } 95 | }); 96 | } 97 | 98 | // On server startup, create some players if the database is empty. 99 | if (Meteor.isServer) { 100 | 101 | Players.publish('allPlayers', function(){ 102 | if(this.search){ 103 | this.search = "(?i)(" + this.search + ").*"; 104 | return "MATCH (node:Player) WHERE node.name =~ {search} RETURN node ORDER BY node.score DESC"; 105 | } 106 | }, function(){ 107 | /* onSubscribe callback */ 108 | if (Players.find({}).count() <= 0) { 109 | var names = [ 110 | 'Ada Lovelace', 111 | 'Grace Hopper', 112 | 'Marie Curie', 113 | 'Carl Friedrich Gauss', 114 | 'Nikola Tesla', 115 | 'Claude Shannon', 116 | 'Ostr.io' 117 | ]; 118 | var players = []; 119 | 120 | for (i = 0, len = names.length; i < len; i++) { 121 | name = names[i]; 122 | players.push({ 123 | name: name, 124 | score: Math.floor(Random.fraction() * 10) * 5, 125 | __labels: ':Player' 126 | }); 127 | } 128 | 129 | Players.insert(players); 130 | 131 | // Meteor.neo4j.query('CREATE (a:Player {players})', {players: players}, function(err){ 132 | // if(err){ 133 | // throw err; 134 | // } 135 | // }); 136 | 137 | // Meteor.N4JDB.query('CREATE (a:Player {players})', {players: players}, function(err){ 138 | // if(err){ 139 | // throw err; 140 | // } 141 | // }); 142 | } 143 | }); 144 | } 145 | -------------------------------------------------------------------------------- /leaderboard.css: -------------------------------------------------------------------------------- 1 | * { 2 | -moz-box-sizing: border-box; 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif; 8 | font-size: 16px; 9 | font-weight: normal; 10 | margin: 3em 0; 11 | padding: 0; 12 | } 13 | 14 | .outer { 15 | margin: 0 auto; 16 | max-width: 480px; 17 | } 18 | 19 | .logo { 20 | background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0ZWQgYnkgSWNvTW9vbi5pbyAtLT4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj4KPGcgaWQ9Imljb21vb24taWdub3JlIj4KCTxsaW5lIHN0cm9rZS13aWR0aD0iMSIgeDE9IiIgeTE9IiIgeDI9IiIgeTI9IiIgc3Ryb2tlPSIjNDQ5RkRCIiBvcGFjaXR5PSIiPjwvbGluZT4KPC9nPgoJPHBhdGggZD0iTTI2LjUyIDI0LjcwNWwtNi41Mi0xMS4yOTN2LTcuODc3aDAuNzVjMC42OSAwIDEuMjUtMC41NiAxLjI1LTEuMjVzLTAuNTYtMS4yNS0xLjI1LTEuMjVoLTkuNWMtMC42OSAwLTEuMjUgMC41Ni0xLjI1IDEuMjVzMC41NiAxLjI1IDEuMjUgMS4yNWgwLjc1djcuODc3bC02LjUyIDExLjI5M2MtMS4zNzUgMi4zODEtMC4yNSA0LjMzIDIuNSA0LjMzaDE2LjA0MGMyLjc1IDAgMy44NzUtMS45NDkgMi41LTQuMzN6TTE0LjUgNS41MzVoM2wtMC4wMDIgOC41NDMgMS45OTYgMy40NTdoLTYuOTljMS4xMjMtMS45NDYgMS45OTYtMy40NTkgMS45OTYtMy40NTl2LTguNTQxek0yNC4wMjAgMjYuNTM1aC0xNi4wNDBjLTAuMjU5IDAtMC40NDMtMC4wMzAtMC41NjEtMC4wNjMgMC4wMzEtMC4xMTkgMC4wOTctMC4yOTQgMC4yMjctMC41MTggMCAwIDEuNzA2LTIuOTU3IDMuNDE2LTUuOTJsOS44NzYtMCAzLjQxOCA1LjkyYzAuMTI5IDAuMjI0IDAuMTk1IDAuMzk5IDAuMjI3IDAuNTE4LTAuMTE4IDAuMDMyLTAuMzAzIDAuMDYzLTAuNTYyIDAuMDYzeiIgZmlsbD0iIzAwMDAwMCI+PC9wYXRoPgo8L3N2Zz4K'); 21 | background-position: center center; 22 | background-repeat: no-repeat; 23 | background-size: contain; 24 | height: 1.5em; 25 | margin: 0 auto .75em; 26 | width: 1.5em; 27 | } 28 | 29 | .title { 30 | font-size: 1.5em; 31 | font-weight: 600; 32 | letter-spacing: .3em; 33 | margin: 0 0 .25em; 34 | text-align: center; 35 | text-indent: .3em; 36 | text-transform: uppercase; 37 | } 38 | 39 | .subtitle { 40 | color: #999; 41 | font-size: .875em; 42 | margin-bottom: 2em; 43 | text-align: center; 44 | } 45 | 46 | .leaderboard { 47 | border-top: 1px solid #f1f1f1; 48 | counter-reset: ol-counter; 49 | list-style-type: none; 50 | margin: 0 0 1.5em; 51 | padding: 0; 52 | } 53 | 54 | .player { 55 | border-bottom: 1px solid #f1f1f1; 56 | cursor: pointer; 57 | padding: .5em 0; 58 | position: relative; 59 | overflow: hidden; 60 | transition: background 300ms ease-out; 61 | border-left: 4px solid white; 62 | } 63 | 64 | .player:before { 65 | color: #999; 66 | content: counter(ol-counter); 67 | counter-increment: ol-counter; 68 | display: inline-block; 69 | font-weight: 300; 70 | line-height: 3em; 71 | text-align: center; 72 | vertical-align: middle; 73 | width: 3em; 74 | } 75 | 76 | .player .avatar { 77 | display: inline-block; 78 | margin-right: 1em; 79 | vertical-align: middle; 80 | } 81 | 82 | .player .name { 83 | display: inline-block; 84 | font-size: 1.25em; 85 | font-weight: 300; 86 | vertical-align: middle; 87 | } 88 | 89 | .player .score { 90 | color: #333; 91 | display: block; 92 | float: right; 93 | font-size: 1.25em; 94 | font-weight: 600; 95 | line-height: 2.4em; 96 | padding-right: 1.25em; 97 | } 98 | 99 | .player.selected { 100 | background-color: #fefff4; 101 | border-left: #eb5f3a 4px solid; 102 | } 103 | 104 | .player:hover { 105 | background-color: #fefff4; 106 | } 107 | 108 | .details { 109 | overflow: hidden; 110 | } 111 | 112 | .details .name { 113 | display: inline-block; 114 | font-size: 1.5em; 115 | font-weight: 300; 116 | line-height: 2.25rem; 117 | padding-left: 1.25rem; 118 | vertical-align: middle; 119 | } 120 | 121 | .details .inc, .button{ 122 | border-radius: 3em; 123 | border: #eb5f3a 1px solid; 124 | background: transparent; 125 | color: #eb5f3a; 126 | cursor: pointer; 127 | float: right; 128 | font-family: 'Source Sans Pro' ,'Helvetica Neue', Helvetica, Arial, sans-serif; 129 | font-size: 1rem; 130 | line-height: 1; 131 | margin: 0; 132 | outline: none; 133 | padding: 10px 30px; 134 | transition: all 200ms ease-in; 135 | } 136 | 137 | .inc:hover { 138 | background: #eb5f3a; 139 | color: #fff; 140 | } 141 | 142 | .inc:active { 143 | box-shadow: rgba(0,0,0,.3) 0 1px 3px 0 inset; 144 | } 145 | 146 | .message { 147 | color: #aaa; 148 | line-height: 2.25rem; 149 | text-align: center; 150 | } 151 | 152 | .input{ 153 | width: 100%; 154 | border-radius: 3em; 155 | border: #eb5f3a 1px solid; 156 | display: inline-block; 157 | font-family: 'Source Sans Pro' ,'Helvetica Neue', Helvetica, Arial, sans-serif; 158 | font-size: 1rem; 159 | line-height: 1; 160 | margin: 0; 161 | outline: none; 162 | padding: 10px 30px; 163 | } 164 | 165 | .input.addPlayer{ 166 | width: 300px; 167 | } 168 | 169 | .removePlayer{ 170 | float: left; 171 | cursor: pointer; 172 | margin: 15px 0px 0px 0px; 173 | } 174 | 175 | @media (max-width: 500px) { 176 | .details, .message { 177 | display: block; 178 | position: fixed; 179 | bottom: 0; 180 | background-color: #fafafa; 181 | width: 100%; 182 | padding: 12px 15px; 183 | border-top: 1px solid #ccc; 184 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); 185 | } 186 | 187 | .details .name { 188 | font-size: 1.2em; 189 | padding-left: 0; 190 | } 191 | 192 | .details .inc { 193 | padding: 10px 20px; 194 | } 195 | 196 | body { 197 | margin: 2em 0 4em 0; 198 | } 199 | 200 | .player:hover { 201 | background-color: inherit; 202 | } 203 | 204 | .player.selected:hover { 205 | background-color: #fefff4; 206 | } 207 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### This is example for [Neo4jreactivity](https://github.com/VeliovGroup/ostrio-Neo4jreactivity) 2 | 3 | From this repository you will find out how to use Neo4j and Meteor within [Neo4jreactivity](https://github.com/VeliovGroup/ostrio-Neo4jreactivity) atmospherejs.com package 4 | 5 | Additional functionality: 6 | - User remove 7 | - User search 8 | 9 | ### Install 10 | ###### Neo4j 11 | Pick up Neo4j version from [Official Neo4j Website](http://neo4j.com/download/) 12 | After download - unzip and place into root folder of your user (name it neo4j), then in Terminal run: 13 | ```bash 14 | cd ~/neo4j #downloaded and replaced folder 15 | bin/neo4j start 16 | ``` 17 | * Note: __Neo4j database should be running before Meteor__ * 18 | 19 | ###### Node.js 20 | Pick up version for your system on [Official Node.js Website](http://nodejs.org/download/) 21 | Follow installer instructions 22 | 23 | ###### Meteor 24 | Run this in Terminal: 25 | ```bash 26 | curl https://install.meteor.com/ | sh 27 | ``` 28 | 29 | ###### Neo4j NPM Package 30 | To install Neo4j NPM Package, run this in Terminal: 31 | ```bash 32 | npm -g install neo4j 33 | ``` 34 | 35 | ###### Clone this repository 36 | Run in Terminal: 37 | ```bash 38 | git clone git@github.com:VeliovGroup/Meteor-Leaderboard-Neo4j.git 39 | cd Meteor-Leaderboard-Neo4j 40 | ``` 41 | 42 | ###### Running meteor 43 | Run a line below in Terminal, inside ```Meteor-Leaderboard-Neo4j``` folder: 44 | ```bash 45 | meteor 46 | ``` 47 | 48 | ###### What next 49 | - In your browser go to [localhost:3000](http://localhost:3000/) 50 | - To see Neo4j browser, go to [localhost:7474](http://localhost:7474/) 51 | 52 | 53 | ------ 54 | 55 | 56 | ### How we rewrite Leaderboard example to be used with Neo4j 57 | In article below you will understand the basics of `ostrio:neo4jreactivity` and `ostrio:neo4driver` packages. How to translate queries and other code from MongoDB to Neo4j. How to write basic query in Neo4j. How to use reactivity in Neo4j. 58 | 59 | #### Meteor’s Leaderboard example app, driven by Neo4j database 60 | We've decided to take very basic example application at Meteor - "Leaderboard" and move it from MongoDB to Neo4j. 61 | 62 | ##### Prepare dev stage: 63 | ###### Create example app 64 | First of all download Meteor and Neo4j onto your working machine, then run: 65 | ```shell 66 | $ meteor create --example leaderboard 67 | $ cd leaderboard 68 | $ meteor 69 | ``` 70 | 71 | ###### Run Neo4j DB 72 | Run inside of Neo4j directory: 73 | ```shell 74 | $ bin/neo4j start 75 | ``` 76 | 77 | ###### Installing Neo4j Meteor’s drivers 78 | ```shell 79 | $ meteor add ostrio:neo4jreactivity 80 | ``` 81 | 82 | ------- 83 | 84 | ##### Writing the code 85 | ###### Understanding the “Leaderboard” sources 86 | 87 | - When server start, it is creates Players 88 | - In template’s helper `players` - all players ordered by score 89 | - In template’s helper `selectedName` - player name comes via `findOne` method 90 | - Incrementation of score implemented via `update` method 91 | 92 | Let’s put our hands on it 93 | 94 | ###### Moving to Neo4j: 95 | At first let’s create neo4j config file `lib/neo4j.js`: 96 | ```javascript 97 | /* Allow client query execution */ 98 | Meteor.neo4j.allowClientQuery = true; 99 | /* Custom URL to Neo4j should be here */ 100 | /* CHANGE THIS LINE IN ACCORDING TO YOUR NEO4J SETTINGS */ 101 | Meteor.neo4j.connectionURL = 'http://user:password@localhost:7474'; 102 | /* But deny all writing actions on client */ 103 | Meteor.neo4j.set.deny(neo4j.rules.write); 104 | ``` 105 | 106 | Let’s move to `leaderboard.js` file and change this line [*Isomorphic*]: 107 | ```javascript 108 | /* Players = new Mongo.Collection("players"); */ 109 | var Players = Meteor.neo4j.collection('players'); 110 | ``` 111 | This is isomorphic code, so we have all data we need inside `Players` variable on both - server and client sides. 112 | 113 | First, we need to publish some data to client [*Client*]: 114 | ```javascript 115 | Players.publish('allPlayers', function(){ 116 | return 'MATCH (node:Player) RETURN node ORDER BY node.score DESC'; 117 | }); 118 | ``` 119 | 120 | And check if we have users or not, if not - create new users [*Server*]: 121 | ```javascript 122 | Players.publish('allPlayers', function(){ 123 | return 'MATCH (node:Player) RETURN node ORDER BY node.score DESC'; 124 | }, function(){ 125 | if (Players.find({}).count() <= 0) { 126 | var names = [ 'Ada Lovelace', 127 | 'Grace Hopper', 128 | 'Marie Curie', 129 | 'Carl Friedrich Gauss', 130 | 'Nikola Tesla', 131 | 'Claude Shannon']; 132 | var players = []; 133 | _.each(names, function (name) { 134 | players.push({ 135 | name: name, 136 | score: Math.floor(Random.fraction() * 10) * 5, 137 | __labels: ':Player' /* Assign Cypher label to all nodes */ 138 | }); 139 | }); 140 | Players.insert(players); 141 | }); 142 | ``` 143 | 144 | To generate Players we can use several methods, all methods below is reactive and works almost in same way, the `players` variable is array of objects {*[Object]*}: 145 | ```javascript 146 | Players.insert(players); 147 | /* or */ 148 | Meteor.neo4j.query('CREATE (a:Player {players})', {players: players}); 149 | /* or */ 150 | Meteor.N4JDB.query('CREATE (a:Player {players})', {players: players}); 151 | ``` 152 | 153 | To increment ‘score’, we will use standard `update` method as we use in mongo [*Client*]: 154 | ```javascript 155 | Players.update({ 156 | _id: Session.get('selectedPlayer') 157 | },{ 158 | '$inc': { 159 | score: 1 160 | } 161 | }); 162 | ``` 163 | 164 | To create new user - we only will add the `__label` property [*Client*]: 165 | ```javascript 166 | Players.insert({ 167 | name: $('#newPlayerName').val(), 168 | score: 0, 169 | __labels: ':Player' 170 | }); 171 | ``` 172 | 173 | To return Players list on client side, we will find all nodes with `Player` label and sort them [*Client*]: 174 | ```javascript 175 | Template.leaderboard.helpers({ 176 | players: function () { 177 | Players.find({'metadata.labels': 'Player'}, { 178 | sort:{ 179 | score: -1 180 | } 181 | }); 182 | }, 183 | ... 184 | }); 185 | ``` 186 | 187 | 188 | --------------------------------------------------------------------------------