├── .gitignore ├── .rspec ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSE.md ├── MIDDLEWARE.md ├── README.md ├── REALTIME.md ├── Rakefile ├── TODO.md ├── bin └── ki ├── ki.gemspec ├── lib ├── ki.rb └── ki │ ├── base_request.rb │ ├── channel_manager.rb │ ├── helpers.rb │ ├── ki.rb │ ├── ki_app.rb │ ├── ki_cli.rb │ ├── ki_config.rb │ ├── ki_logger.rb │ ├── middleware │ ├── admin_interface_generator.rb │ ├── api_handler.rb │ ├── base_middleware.rb │ ├── coffee_compiler.rb │ ├── haml_compiler.rb │ ├── helpers │ │ ├── format_of_helper.rb │ │ ├── haml_compiler_helper.rb │ │ ├── public_file_helper.rb │ │ ├── redirect_to_helper.rb │ │ └── view_helper.rb │ ├── init_middleware.rb │ ├── insta_doc.rb │ ├── public_file_server.rb │ ├── realtime.rb │ └── sass_compiler.rb │ ├── model.rb │ ├── modules │ ├── callbacks.rb │ ├── model_helper.rb │ ├── query_interface.rb │ └── restrictions.rb │ ├── orm.rb │ ├── utils │ ├── annotations.rb │ ├── api_error.rb │ ├── descendants.rb │ ├── extra_irb.rb │ ├── extra_ruby.rb │ ├── indifferent_hash.rb │ ├── logger.rb │ └── redirect_to_helper.rb │ ├── version.rb │ └── views │ ├── 404.haml │ ├── instadmin.haml │ └── instadoc.haml └── spec ├── config.yml.example ├── examples ├── base │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Gemfile │ ├── app.rb │ ├── client.rb │ ├── config.ru │ ├── config.yml │ ├── logs │ │ └── .keep │ ├── public │ │ ├── favicon.ico │ │ ├── javascripts │ │ │ ├── .gitkeep │ │ │ └── app.js │ │ └── stylesheets │ │ │ └── .gitkeep │ └── views │ │ └── index.haml ├── couch-lock │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Gemfile │ ├── app.rb │ ├── config.ru │ ├── config.yml │ ├── install.sh │ ├── public │ │ ├── doorbell-1.wav │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ ├── javascripts │ │ │ ├── .gitkeep │ │ │ ├── angular.min.js │ │ │ └── clApp.coffee │ │ └── stylesheets │ │ │ ├── .gitkeep │ │ │ ├── bootstrap-theme.min.css │ │ │ ├── bootstrap.min.css │ │ │ ├── font-awesome.min.css │ │ │ └── main.sass │ └── views │ │ ├── index.haml │ │ ├── layout.haml │ │ ├── settings.haml │ │ ├── shop.haml │ │ └── wiki.haml ├── json.northpole.ro │ ├── .bowerrc │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Capfile │ ├── Gemfile │ ├── Gemfile.lock │ ├── app.rb │ ├── bower.json │ ├── client.rb │ ├── config.ru │ ├── config.yml │ ├── config.yml.backup │ ├── config │ │ ├── deploy.rb │ │ └── deploy │ │ │ └── production.rb │ ├── foo.rb │ ├── public │ │ ├── JNorthPole.jar │ │ ├── app │ │ │ ├── .bowerrc │ │ │ ├── .gitignore │ │ │ ├── .jshintrc │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── bower.json │ │ │ ├── build │ │ │ │ ├── assets │ │ │ │ │ └── svg │ │ │ │ │ │ ├── avatar-1.svg │ │ │ │ │ │ ├── avatar-4.svg │ │ │ │ │ │ ├── avatars.svg │ │ │ │ │ │ ├── contact_mail_48px.svg │ │ │ │ │ │ ├── google_plus.svg │ │ │ │ │ │ ├── hangouts.svg │ │ │ │ │ │ ├── ic_adb_48px.svg │ │ │ │ │ │ ├── ic_add_48px.svg │ │ │ │ │ │ ├── ic_clear_48px.svg │ │ │ │ │ │ ├── ic_delete_48px.svg │ │ │ │ │ │ ├── ic_lock_48px.svg │ │ │ │ │ │ ├── ic_music_note_48px.svg │ │ │ │ │ │ ├── ic_note_add_48px.svg │ │ │ │ │ │ ├── ic_person_add_48px.svg │ │ │ │ │ │ ├── ic_settings_48px.svg │ │ │ │ │ │ ├── ic_share_48px.svg │ │ │ │ │ │ ├── ic_view_list_48px.svg │ │ │ │ │ │ ├── icon.svg │ │ │ │ │ │ ├── mail.svg │ │ │ │ │ │ ├── manggo.svg │ │ │ │ │ │ ├── menu.svg │ │ │ │ │ │ ├── phone.svg │ │ │ │ │ │ ├── share.svg │ │ │ │ │ │ └── twitter.svg │ │ │ │ ├── css │ │ │ │ │ ├── app.css │ │ │ │ │ └── vendor.css │ │ │ │ ├── index.html │ │ │ │ └── js │ │ │ │ │ ├── MainController.js │ │ │ │ │ ├── app.js │ │ │ │ │ ├── blobs │ │ │ │ │ ├── BlobsController.js │ │ │ │ │ └── blobs.html │ │ │ │ │ ├── tutorial │ │ │ │ │ └── tutorial.html │ │ │ │ │ ├── users │ │ │ │ │ ├── UserService.js │ │ │ │ │ ├── Users.js │ │ │ │ │ └── users.html │ │ │ │ │ └── vendor.js │ │ │ ├── e2e │ │ │ │ ├── pages │ │ │ │ │ ├── ContactUser.js │ │ │ │ │ ├── UserDetails.js │ │ │ │ │ └── UserList.js │ │ │ │ ├── protractor.conf.js │ │ │ │ └── scenarios │ │ │ │ │ └── users.js │ │ │ ├── gulpfile.js │ │ │ ├── karma.conf.js │ │ │ ├── package.json │ │ │ └── src │ │ │ │ ├── assets │ │ │ │ └── svg │ │ │ │ │ ├── avatar-1.svg │ │ │ │ │ ├── avatar-4.svg │ │ │ │ │ ├── avatars.svg │ │ │ │ │ ├── google_plus.svg │ │ │ │ │ ├── hangouts.svg │ │ │ │ │ ├── ic_fullscreen_48px.svg │ │ │ │ │ ├── ic_fullscreen_exit_48px.svg │ │ │ │ │ ├── ic_music_note_48px.svg │ │ │ │ │ ├── ic_note_add_48px.svg │ │ │ │ │ ├── ic_view_list_48px.svg │ │ │ │ │ ├── icon.svg │ │ │ │ │ ├── mail.svg │ │ │ │ │ ├── manggo.svg │ │ │ │ │ ├── menu.svg │ │ │ │ │ ├── phone.svg │ │ │ │ │ ├── share.svg │ │ │ │ │ └── twitter.svg │ │ │ │ ├── css │ │ │ │ ├── app.css │ │ │ │ ├── app.css.map │ │ │ │ └── app.sass │ │ │ │ ├── index.html │ │ │ │ └── js │ │ │ │ ├── MainController.coffee │ │ │ │ ├── app.coffee │ │ │ │ ├── blobs │ │ │ │ ├── BlobsController.coffee │ │ │ │ └── blobs.html │ │ │ │ ├── tutorial │ │ │ │ └── tutorial.html │ │ │ │ └── users │ │ │ │ ├── UserService.coffee │ │ │ │ ├── Users.coffee │ │ │ │ └── users.html │ │ ├── favicon.ico │ │ ├── font │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── images │ │ │ ├── bear.png │ │ │ ├── bg.png │ │ │ ├── cloud.png │ │ │ ├── dotnet.png │ │ │ ├── dotnet_small.png │ │ │ ├── footer.png │ │ │ ├── glyphicons-halflings-white.png │ │ │ ├── glyphicons-halflings.png │ │ │ ├── ice.png │ │ │ ├── java.png │ │ │ ├── java_small.png │ │ │ ├── js.png │ │ │ ├── json.png │ │ │ ├── json_small.png │ │ │ ├── logo.png │ │ │ ├── mobile.png │ │ │ ├── php.png │ │ │ ├── php_small.png │ │ │ ├── python.png │ │ │ ├── python_small.png │ │ │ ├── ruby.png │ │ │ ├── ruby_small.png │ │ │ ├── shell.png │ │ │ └── shell_small.png │ │ ├── javascripts │ │ │ ├── analytics.js │ │ │ ├── app.coffee │ │ │ ├── jnorthpole.coffee │ │ │ └── realtime.coffee │ │ ├── music │ │ │ ├── angular-youtube-embed.js │ │ │ ├── index.html │ │ │ ├── music.coffee │ │ │ └── music.sass │ │ └── stylesheets │ │ │ └── app.sass │ └── views │ │ ├── curl.haml │ │ ├── doc.haml │ │ ├── doc_table.haml │ │ ├── dotnet.haml │ │ ├── examples.haml │ │ ├── faq.haml │ │ ├── index.haml │ │ ├── java.haml │ │ ├── javascript.haml │ │ ├── layout.haml │ │ ├── music.haml │ │ ├── php.haml │ │ ├── playground.haml │ │ ├── python.haml │ │ ├── ruby.haml │ │ ├── seed.haml │ │ ├── thanks.haml │ │ └── websocket.haml ├── tasks-example │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Gemfile │ ├── README.md │ ├── app.rb │ ├── config.ru │ ├── config.yml │ ├── public │ │ ├── favicon.ico │ │ ├── javascripts │ │ │ └── .gitkeep │ │ └── stylesheets │ │ │ └── .gitkeep │ ├── tasks │ │ └── hello_world.rb │ └── views │ │ └── index.haml └── todo │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Gemfile │ ├── app.rb │ ├── config.ru │ ├── config.yml │ ├── public │ ├── favicon.ico │ ├── javascripts │ │ └── .gitkeep │ └── stylesheets │ │ └── .gitkeep │ └── views │ ├── index.haml │ └── layout.haml ├── functional_spec.rb ├── lib └── ki │ ├── base_request_spec.rb │ ├── channel_manager_spec.rb │ ├── helpers_spec.rb │ ├── indifferent_hash_spec.rb │ ├── ki_app_spec.rb │ ├── ki_config_spec.rb │ ├── ki_spec.rb │ ├── middleware │ ├── admin_generator_spec.rb │ ├── haml_compiler_spec.rb │ ├── helpers │ │ └── format_of_helper_spec.rb │ ├── init_middleware_spec.rb │ ├── insta_doc_spec.rb │ └── realtime_spec.rb │ ├── middleware_spec.rb │ ├── model_spec.rb │ ├── modules │ ├── model_helper_spec.rb │ └── restrictions_spec.rb │ ├── orm_spec.rb │ └── utils │ └── api_error_spec.rb ├── spec_helper.rb └── util_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /pkg/ 4 | /spec/reports/ 5 | /spec/config.yml 6 | /tmp/ 7 | 8 | /.bundle/ 9 | 10 | spec/examples/*/tmp 11 | spec/examples/*/log 12 | spec/examples/*/Gemfile.lock 13 | 14 | /doc/* 15 | coverage/ 16 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - 'spec/examples/**/*' 4 | 5 | Style/BracesAroundHashParameters: 6 | Enabled: false 7 | 8 | Style/Documentation: 9 | Enabled: false 10 | 11 | Style/BlockDelimiters: 12 | Enabled: false 13 | 14 | Metrics/LineLength: 15 | Enabled: false 16 | 17 | Metrics/MethodLength: 18 | Enabled: false 19 | 20 | Metrics/AbcSize: 21 | Enabled: false 22 | 23 | Lint/UnusedMethodArgument: 24 | Enabled: false 25 | 26 | Metrics/BlockLength: 27 | Enabled: false 28 | 29 | Style/ClassAndModuleChildren: 30 | Enabled: false 31 | 32 | Style/MethodMissing: 33 | Enabled: false 34 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | ki2 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.3 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.4.3 4 | services: 5 | - mongodb 6 | before_script: 7 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 8 | - chmod +x ./cc-test-reporter 9 | - ./cc-test-reporter before-build 10 | script: 11 | - bundle exec rspec 12 | after_script: 13 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT 14 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | guard :rspec, cmd: 'bundle exec rspec' do 4 | require 'guard/rspec/dsl' 5 | dsl = Guard::RSpec::Dsl.new(self) 6 | 7 | # RSpec files 8 | rspec = dsl.rspec 9 | watch(rspec.spec_helper) { rspec.spec_dir } 10 | watch(rspec.spec_support) { rspec.spec_dir } 11 | watch(rspec.spec_files) 12 | 13 | # Ruby files 14 | ruby = dsl.ruby 15 | dsl.watch_spec_files_for(ruby.lib_files) 16 | end 17 | 18 | guard :rubocop do 19 | watch(/%r{.+\.rb$}/) 20 | watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) } 21 | end 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Cristian Mircea Messel 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 | -------------------------------------------------------------------------------- /MIDDLEWARE.md: -------------------------------------------------------------------------------- 1 | # Ki Framework 2 | 3 | Ki uses middleware to provide a plug-n-play way of customizing requests. 4 | Rack handles everything™ 5 | 6 | ## Middleware 7 | 8 | It is configured in the config.yml of the application. 9 | 10 | The default enabled middleware list: 11 | 12 | * ApiHandler 13 | * CoffeeCompiler 14 | * SassCompiler 15 | * HamlCompiler 16 | * PublicFileServer 17 | 18 | ### ApiHandler 19 | 20 | Handles all api calls. 21 | 22 | ### CoffeeCompiler 23 | 24 | On request, compiles coffee to js. 25 | 26 | For example, if you have a file *public/javascripts/app.coffee*, accessing 27 | */public/javascripts/app.js* will return the compiled coffee file. 28 | 29 | ### SassCompiler 30 | 31 | Similar to CoffeeCompiler, compiles sass to css. 32 | 33 | ### HamlCompiler 34 | 35 | Similar to CoffeeCompiler, compiles haml to html. 36 | 37 | ### PublicFileServer 38 | 39 | Serves static files from the *public/* folder. 40 | 41 | ### example middleware configuration 42 | 43 | ```yml 44 | development: 45 | database: 46 | name: base_development 47 | host: 127.0.0.1 48 | port: 27017 49 | add_middleware: ['Realtime', 'InstaDoc', 'AdminInterfaceGenerator'] 50 | rm_middleware: ['SassCompiler'] 51 | 52 | test: 53 | database: 54 | name: np_test 55 | host: 127.0.0.1 56 | port: 27017 57 | 58 | production: 59 | middleware: ['ApiHandler', 'PublicFileServer'] 60 | add_middleware: ['Realtime', 'InstaDoc', 'AdminInterfaceGenerator'] 61 | database: 62 | name: np 63 | host: 127.0.0.1 64 | port: 27017 65 | ``` 66 | -------------------------------------------------------------------------------- /REALTIME.md: -------------------------------------------------------------------------------- 1 | # Realtime 2 | 3 | The following webservers are supported: 4 | 5 | * Goliath 6 | * Phusion Passenger >= 4.0 with nginx >= 1.4 7 | * Puma >= 2.0 8 | * Rainbows 9 | * Thin 10 | 11 | ## Requrirements 12 | 13 | * Load the adapter for your webserver in config.ru after 'require' (example: Faye::WebSocket.load_adapter('thin')) 14 | * You need to run in production mode (example: rackup -p 1337 -E production) 15 | * add the Realtime middlware in config.yml (example: add_middleware: 'Realtime') 16 | 17 | ## Documentation 18 | 19 | Ki realtime offers channels to which a user subscribes to. Once a user is subscribed 20 | to a channel he/she will receive messages from that channel. Anybody can publish 21 | messages in a channel. 22 | 23 | Upon connecting, the user will receive a unique id. 24 | 25 | Using WebSockets, connect to the /realtime endpoint: 26 | 27 | ```javascript 28 | // connect 29 | var socket = new WebSocket("ws://localhost:1337/realtime"); 30 | socket.onclose = function() { console.log('socket closed'); } 31 | socket.onmessage = function(m) { 32 | json = JSON.parse(m) 33 | console.log(json); 34 | } 35 | 36 | send = function (hash) { 37 | socket.send(JSON.stringify(hash)) 38 | } 39 | 40 | // Subscribe to channel events 41 | send({type: 'subscribe', channel_name: 'my-channel'}) 42 | 43 | // Unsubscribe from channel events 44 | send({type: 'unsubscribe', channel_name: 'my-channel'}) 45 | 46 | // Send a message to a channel 47 | send({type: 'publish', channel_name: 'my-channel', text: 'asd', custom_attribute: [1, 'text']}) 48 | ``` 49 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rspec/core/rake_task' 5 | require 'rdoc/task' 6 | 7 | RDoc::Task.new do |rdoc| 8 | rdoc.main = 'README.md' 9 | rdoc.rdoc_files 10 | .include('README.md', 'lib/**/*.rb') 11 | rdoc.rdoc_dir = 'doc' 12 | end 13 | 14 | RSpec::Core::RakeTask.new(:spec) 15 | 16 | task default: :spec 17 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # deploy new version to rubygems 2 | # add logging 3 | # respect RESTful routing /foo/:id is the only thing missing 4 | 5 | if param is set to redirect_to, ki will redirect 6 | 7 | class Redirector < Ki::Model 8 | def after_find 9 | @params['redirect_to'] = 'http://localhost:1337/' 10 | end 11 | end> 12 | 13 | how to read headers 14 | 15 | class Headers < Ki::Model 16 | def after_find 17 | @result = @req.headers 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /bin/ki: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 5 | 6 | require 'ki/ki_cli' 7 | 8 | Ki::Cli::Main.start(ARGV) 9 | -------------------------------------------------------------------------------- /ki.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('../lib', __FILE__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'ki/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'ki' 9 | spec.version = Ki::VERSION 10 | spec.authors = ['Cristian Mircea Messel'] 11 | spec.email = ['mess110@gmail.com'] 12 | spec.summary = 'it said optional' 13 | spec.description = 'it said optional' 14 | spec.homepage = 'https://github.com/mess110/ki' 15 | spec.license = 'MIT' 16 | 17 | spec.files = `git ls-files -z`.split("\x0") 18 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 19 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 20 | spec.require_paths = %w[lib examples] 21 | 22 | spec.add_runtime_dependency 'bson_ext' 23 | spec.add_runtime_dependency 'bundler' 24 | spec.add_runtime_dependency 'coffee-script' 25 | spec.add_runtime_dependency 'faye-websocket' 26 | spec.add_runtime_dependency 'haml' 27 | spec.add_runtime_dependency 'mongo' 28 | spec.add_runtime_dependency 'rack' 29 | spec.add_runtime_dependency 'rack-cors' 30 | spec.add_runtime_dependency 'rack-parser' 31 | spec.add_runtime_dependency 'sass' 32 | spec.add_runtime_dependency 'thor' 33 | 34 | spec.add_development_dependency 'guard-rspec' 35 | spec.add_development_dependency 'guard-rubocop' 36 | spec.add_development_dependency 'rack-test' 37 | spec.add_development_dependency 'rake' 38 | spec.add_development_dependency 'rspec' 39 | spec.add_development_dependency 'simplecov' 40 | end 41 | -------------------------------------------------------------------------------- /lib/ki.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ruby stdlib 4 | require 'yaml' 5 | require 'uri' 6 | 7 | # gems 8 | require 'rack' 9 | require 'rack/parser' 10 | require 'rack/cors' 11 | require 'haml' 12 | require 'sass' 13 | require 'coffee-script' 14 | require 'mongo' 15 | require 'faye/websocket' 16 | require 'eventmachine' 17 | require 'logger' 18 | 19 | # code 20 | require 'ki/utils/annotations' 21 | require 'ki/utils/descendants' 22 | require 'ki/utils/api_error' 23 | require 'ki/utils/extra_ruby' 24 | require 'ki/utils/logger' 25 | require 'ki/utils/indifferent_hash' 26 | 27 | require 'ki/modules/query_interface' 28 | require 'ki/modules/restrictions' 29 | require 'ki/modules/callbacks' 30 | require 'ki/modules/model_helper' 31 | 32 | require 'ki/middleware/helpers/format_of_helper' 33 | require 'ki/middleware/helpers/public_file_helper' 34 | require 'ki/middleware/helpers/haml_compiler_helper' 35 | require 'ki/middleware/helpers/redirect_to_helper' 36 | require 'ki/middleware/helpers/view_helper' 37 | 38 | require 'ki/middleware/base_middleware' 39 | require 'ki/middleware/init_middleware' 40 | require 'ki/middleware/api_handler' 41 | require 'ki/middleware/public_file_server' 42 | require 'ki/middleware/sass_compiler' 43 | require 'ki/middleware/haml_compiler' 44 | require 'ki/middleware/coffee_compiler' 45 | require 'ki/middleware/insta_doc' 46 | require 'ki/middleware/admin_interface_generator' 47 | require 'ki/middleware/realtime' 48 | 49 | require 'ki/ki_config' 50 | require 'ki/ki_logger' 51 | require 'ki/helpers' 52 | require 'ki/orm' 53 | require 'ki/model' 54 | require 'ki/channel_manager' 55 | require 'ki/base_request' 56 | require 'ki/ki_app' 57 | 58 | require 'ki/ki' 59 | -------------------------------------------------------------------------------- /lib/ki/base_request.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class BaseRequest < Rack::Request 5 | include Middleware::Helpers::FormatOf 6 | 7 | def root? 8 | path == '/' 9 | end 10 | 11 | def doc? 12 | path == '/instadoc' 13 | end 14 | 15 | def admin? 16 | path == '/instadmin' 17 | end 18 | 19 | def json? 20 | content_type == 'application/json' || format_of(path) == 'json' 21 | end 22 | 23 | def headers 24 | Hash[*env.select { |k, _v| k.start_with? 'HTTP_' } 25 | .collect { |k, v| [k.sub(/^HTTP_/, ''), v] } 26 | .sort 27 | .flatten] 28 | end 29 | 30 | def to_ki_model_class 31 | path.to_s.delete('/').gsub(format_of(path), '').delete('.').to_class 32 | end 33 | 34 | def to_action 35 | case request_method 36 | when 'GET' 37 | :find 38 | when 'POST' 39 | :create 40 | when 'PUT' 41 | :update 42 | when 'DELETE' 43 | :delete 44 | when 'SEARCH' 45 | :find 46 | else 47 | raise 'unkown action' 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/ki/channel_manager.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class ChannelManager 5 | def self.connect 6 | db.insert 'realtime_channel_sockets', {} 7 | end 8 | 9 | def self.disconnect(socket) 10 | db.delete 'realtime_channel_sockets', { 'id' => socket['id'] } 11 | db.delete 'realtime_channel_subscriptions', { 'socket_id' => socket['id'] } 12 | end 13 | 14 | def self.sockets 15 | db.find 'realtime_channel_sockets' 16 | end 17 | 18 | def self.subscribe(json) 19 | channels = db.find 'realtime_channel_subscriptions', json 20 | if channels.empty? 21 | item = db.insert 'realtime_channel_subscriptions', json 22 | channels.push item 23 | end 24 | channels 25 | end 26 | 27 | def self.unsubscribe(json) 28 | json.delete('type') 29 | db.delete 'realtime_channel_subscriptions', json 30 | end 31 | 32 | def self.publish(json) 33 | json['created_at'] = Time.now.to_i 34 | db.insert 'realtime_channel_messages', json 35 | end 36 | 37 | def self.tick(json) 38 | subscribed_channels = db.find 'realtime_channel_subscriptions', json 39 | channel_names = subscribed_channels.map { |sc| sc['channel_name'] } 40 | 41 | t = Time.now.to_i 42 | 43 | messages = db.find 'realtime_channel_messages', { 44 | 'channel_name' => { '$in' => channel_names }, 45 | 'created_at' => { '$gt' => t - 5 } 46 | } 47 | messages 48 | end 49 | 50 | def self.cleanup 51 | db.delete 'realtime_channel_sockets', {} 52 | db.delete 'realtime_channel_subscriptions', {} 53 | db.delete 'realtime_channel_messages', {} 54 | end 55 | 56 | def self.db 57 | Orm::Db.instance 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/ki/helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Helpers 5 | include Middleware::Helpers::View 6 | 7 | def css(url) 8 | haml "%link{:href => '#{url}', :rel => 'stylesheet'}" 9 | end 10 | 11 | def js(url) 12 | haml "%script{:src => '#{url}'}" 13 | end 14 | 15 | def haml(s) 16 | Haml::Engine.new(s).render 17 | end 18 | 19 | def partial(s) 20 | path = view_path(s) 21 | raise PartialNotFoundError, path unless File.file?(path) 22 | haml(File.read(path)) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/ki/ki.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Ki 5 | PUBLIC_PATH = 'public' 6 | VIEWS_PATH = 'views' 7 | 8 | def initialize 9 | Ki.connect 10 | 11 | @app = Rack::Builder.new do 12 | use Middleware::InitMiddleware 13 | 14 | if Dir.exist?('logs') 15 | logfile = ::File.join('logs', 'requests.log') 16 | logger = ::Logger.new(logfile, 'weekly') 17 | KiLogger.instance.init(logger) 18 | use Rack::CommonLogger, logger 19 | end 20 | 21 | use Rack::Parser, content_types: { 22 | 'application/json' => proc { |body| ::MultiJson.decode body } 23 | } 24 | if KiConfig.instance.cors? 25 | use Rack::Cors do 26 | allow do 27 | origins '*' 28 | resource '*', headers: :any, methods: %i[get search put post delete] 29 | end 30 | end 31 | end 32 | KiConfig.instance.middleware.each do |middleware| 33 | use middleware 34 | end 35 | run KiApp.new 36 | end 37 | end 38 | 39 | def self.connect 40 | KiConfig.instance.read Ki.environment 41 | Orm::Db.instance.establish_connection 42 | end 43 | 44 | def self.environment 45 | ENV['RACK_ENV'] || 'development' 46 | end 47 | 48 | def call(env) 49 | @app.call env 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/ki/ki_app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class KiApp 5 | include Middleware::Helpers::HamlCompiler 6 | include Middleware::Helpers::View 7 | 8 | def call(env) 9 | path = view_exists?('404') ? view_path('404') : custom_view_path 10 | html = render_haml_file(path).strip! 11 | Rack::Response.new(html, 404).finish 12 | end 13 | 14 | def custom_view_path 15 | File.join(File.dirname(__FILE__), 'views', '404.haml') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/ki/ki_config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'singleton' 4 | 5 | module Ki 6 | class KiConfig 7 | include Singleton 8 | 9 | attr_reader :config, :environment 10 | attr_accessor :logger 11 | 12 | def read(environment) 13 | @environment = environment 14 | @config = YAML.load_file(config_file_path)[environment] 15 | @config['cors'] ||= true 16 | end 17 | 18 | def config_file_path 19 | 'config.yml' 20 | end 21 | 22 | def cors? 23 | @config['cors'] 24 | end 25 | 26 | def middleware 27 | used_middleware = %w[ApiHandler CoffeeCompiler SassCompiler HamlCompiler 28 | PublicFileServer] 29 | used_middleware = @config['middleware'] if @config['middleware'] 30 | 31 | used_middleware = add_rm_middleware used_middleware, 'add_middleware', 'push' 32 | used_middleware = add_rm_middleware used_middleware, 'rm_middleware', 'delete' 33 | 34 | # convert middleware to ruby object 35 | used_middleware.uniq.map do |middleware| 36 | Object.const_get('Ki').const_get('Middleware').const_get(middleware) 37 | end 38 | end 39 | 40 | def database 41 | @config['database'] 42 | end 43 | 44 | private 45 | 46 | def add_rm_middleware(used_middleware, key, action) 47 | if @config.key?(key) 48 | # TODO: concat should work here 49 | @config[key] = [@config[key]] if @config[key].class != Array 50 | @config[key].each do |mid| 51 | used_middleware.send(action, mid) 52 | end 53 | end 54 | used_middleware 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /lib/ki/ki_logger.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'singleton' 4 | 5 | module Ki 6 | class KiLogger 7 | include Singleton 8 | 9 | attr_accessor :logger 10 | 11 | def init(logger) 12 | @logger = logger 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ki/middleware/admin_interface_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | class AdminInterfaceGenerator < InstaDoc 6 | def custom_view_path 7 | File.join(File.dirname(__FILE__), '..', 'views', 'instadmin.haml') 8 | end 9 | 10 | def custom_check(req) 11 | req.admin? 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ki/middleware/api_handler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | # Handles all API calls 6 | # 7 | # Any json request is considered an api call. A request is considered as json 8 | # if the format is .json or Content-Type header is set to 'application/json' 9 | # 10 | # If the query param 'redirect_to' is given, the response will not contain the 11 | # json output from the url, instead it will redirect to the url given 12 | class ApiHandler 13 | include BaseMiddleware 14 | include Helpers::RedirectTo 15 | 16 | def call(env) 17 | req = BaseRequest.new env 18 | if req.json? 19 | resourcerize(req) 20 | else 21 | @app.call env 22 | end 23 | end 24 | 25 | def resourcerize(req) 26 | klass = req.to_ki_model_class 27 | 28 | unless Model.descendants.include?(klass) 29 | raise InvalidUrlError.new("invalid url '#{req.path}'", 404) 30 | end 31 | 32 | model = klass.new(req) 33 | render model 34 | rescue ApiError => e 35 | render e 36 | end 37 | 38 | def render(model) 39 | if model.is_a?(ApiError) || model.params['redirect_to'].nil? 40 | resp = Rack::Response.new(model.result.to_json, model.status) 41 | resp['Content-Type'] = 'application/json' 42 | resp.finish 43 | else 44 | redirect_to model.params['redirect_to'] 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/ki/middleware/base_middleware.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | module BaseMiddleware 6 | include Helpers::FormatOf 7 | include Helpers::View 8 | include Helpers::PublicFile 9 | 10 | def initialize(app) 11 | @app = app 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ki/middleware/coffee_compiler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | class CoffeeCompiler 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | coffee_path = req.path.to_s[0...-3] + '.coffee' 11 | if !public_file_exists?(req) && format_of(req) == 'js' && public_file_exists?(coffee_path) 12 | js = CoffeeScript.compile(File.read(public_file_path(coffee_path))) 13 | Rack::Response.new(js, 200, { 'Content-Type' => 'application/javascript' }).finish 14 | else 15 | @app.call env 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ki/middleware/haml_compiler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | class HamlCompiler 6 | include BaseMiddleware 7 | include Helpers::HamlCompiler 8 | 9 | def call(env) 10 | req = BaseRequest.new env 11 | if view_exists?(req) 12 | html = render_haml_file view_path(req) 13 | Rack::Response.new(html).finish 14 | else 15 | @app.call env 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/ki/middleware/helpers/format_of_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | module Helpers 6 | module FormatOf 7 | def format_of(uri) 8 | uri = uri.path if uri.class == BaseRequest 9 | File.extname(URI.parse(uri).path).delete('.') 10 | rescue URI::InvalidURIError 11 | '' 12 | end 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/ki/middleware/helpers/haml_compiler_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | module Helpers 6 | module HamlCompiler 7 | def render_haml_file(file_path, layout = true) 8 | file_contents = File.read(file_path) 9 | 10 | layout_contents = if layout && view_exists?('layout') 11 | File.read(view_path('layout')) 12 | else 13 | '= yield' 14 | end 15 | 16 | html = render_haml(layout_contents).render do 17 | render_haml(file_contents).render 18 | end 19 | 20 | html 21 | end 22 | 23 | def render_haml(s) 24 | Haml::Engine.new("- extend Ki::Helpers\n" + s) 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/ki/middleware/helpers/public_file_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | module Helpers 6 | module PublicFile 7 | def public_file_exists?(path) 8 | path = path.path if path.class == BaseRequest 9 | File.file?(public_file_path(path)) 10 | end 11 | 12 | def public_file_path(path) 13 | path = path.path if path.class == BaseRequest 14 | File.join(Ki::PUBLIC_PATH, path) 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/ki/middleware/helpers/redirect_to_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | module Helpers 6 | module RedirectTo 7 | def redirect_to(path) 8 | resp = Rack::Response.new 9 | resp.redirect(path) 10 | resp.finish 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/ki/middleware/helpers/view_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | module Helpers 6 | module View 7 | def view_exists?(path) 8 | path = path.path if path.class == BaseRequest 9 | File.file?(view_path(path)) 10 | end 11 | 12 | def view_path(path) 13 | path = path.path if path.class == BaseRequest 14 | File.join(Ki::VIEWS_PATH, path + '.haml') 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/ki/middleware/init_middleware.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | class InitMiddleware 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | if req.root? 11 | if public_file_exists? 'index.html' 12 | env['PATH_INFO'] = '/index.html' 13 | Rack::File.new(Ki::PUBLIC_PATH).call env 14 | else 15 | resp = Rack::Response.new 16 | resp.redirect('/index') 17 | resp.finish 18 | end 19 | else 20 | env['CONTENT_TYPE'] = 'application/json' if format_of(req) == 'json' 21 | @app.call env 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/ki/middleware/insta_doc.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware 5 | class InstaDoc < HamlCompiler 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | if custom_check(req) 11 | html = if view_exists?(req) 12 | render_haml_file view_path(req) 13 | else 14 | render_haml_file custom_view_path 15 | end 16 | Rack::Response.new(html).finish 17 | else 18 | @app.call env 19 | end 20 | end 21 | 22 | def custom_check(req) 23 | req.doc? 24 | end 25 | 26 | def custom_view_path 27 | File.join(File.dirname(__FILE__), '..', 'views', 'instadoc.haml') 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/ki/middleware/public_file_server.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | class PublicFileServer 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | if public_file_exists? req 11 | Rack::File.new(Ki::PUBLIC_PATH).call env 12 | else 13 | @app.call env 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/ki/middleware/realtime.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | class Realtime 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | if req.path.to_s == '/realtime/info' 11 | show_stats 12 | elsif req.path.to_s == '/realtime' && Faye::WebSocket.websocket?(env) 13 | handle_websocket env 14 | else 15 | @app.call env 16 | end 17 | end 18 | 19 | def ws_send(ws, hash) 20 | ws.send(hash.to_json.to_s) 21 | end 22 | 23 | def show_stats 24 | hash = { 25 | sockets: ::Ki::ChannelManager.sockets 26 | } 27 | resp = Rack::Response.new(hash.to_json, 200) 28 | resp['Content-Type'] = 'application/json' 29 | resp.finish 30 | end 31 | 32 | def handle_websocket(env) 33 | ws = Faye::WebSocket.new(env) 34 | 35 | socket = ::Ki::ChannelManager.connect 36 | ws_send(ws, socket) 37 | 38 | ws.on :message do |event| 39 | on_message(ws, socket, event.data) 40 | end 41 | 42 | timer = EventMachine::PeriodicTimer.new(1) do 43 | msgs = ::Ki::ChannelManager.tick(socket_id: socket['id']) 44 | ws_send(ws, { messages: msgs }) if msgs.count.positive? 45 | end 46 | 47 | ws.on :close do # |event| 48 | timer.cancel 49 | ::Ki::ChannelManager.disconnect socket 50 | ws = nil 51 | end 52 | 53 | ws.rack_response 54 | end 55 | 56 | def on_message(ws, socket, data) 57 | json = JSON.parse(data) 58 | json['socket_id'] = socket['id'] 59 | if json['type'] == 'subscribe' 60 | channel_manager_action(json, ws, 'subscribe') 61 | elsif json['type'] == 'unsubscribe' 62 | output = ::Ki::ChannelManager.unsubscribe json 63 | ws_send(ws, output) 64 | elsif json['type'] == 'publish' 65 | channel_manager_action(json, ws, 'publish') 66 | else 67 | ws_send(ws, { message: 'Please specify a valid type' }) 68 | end 69 | rescue JSON::ParserError 70 | ws_send(ws, { message: 'Please send a valid json string' }) 71 | end 72 | 73 | def channel_manager_action(json, ws, action) 74 | if json['channel_name'] 75 | output = ::Ki::ChannelManager.send(action, json) 76 | ws_send(ws, output) 77 | else 78 | ws_send(ws, { message: 'Please specify a channel_name' }) 79 | end 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/ki/middleware/sass_compiler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Middleware #:nodoc: 5 | class SassCompiler 6 | include BaseMiddleware 7 | 8 | def call(env) 9 | req = BaseRequest.new env 10 | sass_path = req.path.to_s[0...-4] + '.sass' 11 | # if req ends with css and it does not exist, if a sass file exists instead 12 | if !public_file_exists?(req) && format_of(req) == 'css' && public_file_exists?(sass_path) 13 | eng = Sass::Engine.new(File.read(public_file_path(sass_path)), syntax: :sass) 14 | Rack::Response.new(eng.render, 200, { 'Content-Type' => 'text/css' }).finish 15 | else 16 | @app.call env 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/ki/model.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Model 5 | extend Descendants 6 | extend QueryInterface 7 | extend Restrictions 8 | include Callbacks 9 | include ModelHelper 10 | include Middleware::Helpers::RedirectTo 11 | 12 | annotate! 13 | 14 | attr_accessor :action, :result, :params, :status, :req 15 | 16 | def initialize(req) 17 | @req = req 18 | @action = req.to_action 19 | @params = req.params 20 | @status = 200 21 | 22 | raise ForbiddenAction if forbidden_actions.include? @action 23 | 24 | ccall 25 | end 26 | 27 | def find 28 | @result = self.class.find @params 29 | end 30 | 31 | def create 32 | check_for_required_attributes 33 | check_for_unique_attributes 34 | @result = self.class.create @params 35 | end 36 | 37 | def update 38 | check_for_required_attributes 39 | check_for_unique_attributes 40 | @result = self.class.update @params 41 | end 42 | 43 | def delete 44 | @result = self.class.delete @params 45 | end 46 | 47 | private 48 | 49 | def check_for_required_attributes 50 | required_attributes.each do |ra| 51 | unless @params.keys.include?(ra.to_s) 52 | raise RequiredAttributeMissing, "#{ra} missing" 53 | end 54 | end 55 | end 56 | 57 | def check_for_unique_attributes 58 | unique_attributes.each do |ua| 59 | u = self.class.find({ ua.to_s => @params[ua.to_s] }) 60 | raise AttributeNotUnique, "#{ua} not unique" unless u.empty? 61 | end 62 | end 63 | 64 | def ccall 65 | before_all 66 | send "before_#{@action}".to_sym 67 | send @action.to_sym 68 | send "after_#{@action}".to_sym 69 | after_all 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/ki/modules/callbacks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Model 5 | module Callbacks 6 | def before_all; end 7 | 8 | def before_find; end 9 | 10 | def after_find; end 11 | 12 | def before_create; end 13 | 14 | def after_create; end 15 | 16 | def before_update; end 17 | 18 | def after_update; end 19 | 20 | def before_delete; end 21 | 22 | def after_delete; end 23 | 24 | def after_all; end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/ki/modules/model_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Model 5 | module ModelHelper 6 | def get? 7 | @req.get? 8 | end 9 | 10 | def post? 11 | @req.post? 12 | end 13 | 14 | def put? 15 | @req.put? 16 | end 17 | 18 | def delete? 19 | @req.delete? 20 | end 21 | 22 | def forbidden_actions 23 | [] 24 | end 25 | 26 | def required_attributes 27 | [] 28 | end 29 | 30 | def skipped_params 31 | [] 32 | end 33 | 34 | def unique_attributes 35 | [] 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/ki/modules/query_interface.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Model 5 | # the query interface does not respect before/after filters, 6 | # unique attributes, required attributes or anything of the 7 | # sort. 8 | # it writes directly to the database 9 | module QueryInterface 10 | def count(hash = {}) 11 | Orm::Db.instance.count class_name, hash 12 | end 13 | 14 | def find(hash = {}) 15 | Orm::Db.instance.find class_name, hash 16 | end 17 | 18 | def create(hash) 19 | Orm::Db.instance.insert class_name, hash 20 | end 21 | 22 | def find_or_create(hash) 23 | r = find hash 24 | r.empty? ? create(hash) : r 25 | end 26 | 27 | def update(hash) 28 | Orm::Db.instance.update class_name, hash 29 | end 30 | 31 | def delete(hash) 32 | Orm::Db.instance.delete class_name, hash 33 | end 34 | 35 | def class_name 36 | to_s 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/ki/modules/restrictions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class Model 5 | module Restrictions 6 | def forbidden_actions 7 | [] 8 | end 9 | 10 | def forbid(*actions) 11 | generic_restriction :forbidden_actions, actions 12 | end 13 | 14 | def required_attributes 15 | [] 16 | end 17 | 18 | def requires(*attributes) 19 | generic_restriction :required_attributes, attributes 20 | end 21 | 22 | def unique_attributes 23 | [] 24 | end 25 | 26 | def unique(*attributes) 27 | generic_restriction :unique_attributes, attributes 28 | end 29 | 30 | private 31 | 32 | def generic_restriction(method_name, attributes) 33 | attributes += send(method_name) if defined? method_name 34 | %i[define_method define_singleton_method].each do |definition_means| 35 | send definition_means, method_name do 36 | attributes.sort.uniq 37 | end 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/ki/utils/annotations.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # https://stackoverflow.com/questions/3157426/how-to-simulate-java-like-annotations-in-ruby 4 | module Ki 5 | module Annotations 6 | def annotations(meth = nil) 7 | return @__annotations__[meth] if meth 8 | @__annotations__ 9 | end 10 | 11 | private 12 | 13 | def method_added(m) 14 | (@__annotations__ ||= {})[m] = @__last_annotation__ if @__last_annotation__ 15 | @__last_annotation__ = nil 16 | super 17 | end 18 | 19 | def method_missing(meth, *args) 20 | return super unless /\A_/.match?(meth) 21 | @__last_annotation__ ||= {} 22 | @__last_annotation__[meth[1..-1].to_sym] = args.size == 1 ? args.first : args 23 | end 24 | end 25 | end 26 | 27 | class Module 28 | private 29 | 30 | def annotate! 31 | extend Ki::Annotations 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/ki/utils/api_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | class ApiError < StandardError #:nodoc: 5 | extend Descendants 6 | 7 | attr_reader :status 8 | 9 | def initialize(body = nil, status = 400) 10 | super body.nil? ? to_s : body 11 | @status = status 12 | end 13 | 14 | def result 15 | { 16 | 'error' => to_s, 17 | 'status' => @status 18 | } 19 | end 20 | end 21 | 22 | class InvalidUrlError < ApiError #:nodoc: 23 | end 24 | 25 | class RequiredAttributeMissing < ApiError #:nodoc: 26 | end 27 | 28 | class AttributeNotUnique < ApiError #:nodoc: 29 | end 30 | 31 | class ForbiddenAction < ApiError #:nodoc: 32 | def initialize(s = 'forbidden', code = 403) 33 | super s, code 34 | end 35 | end 36 | 37 | class UnauthorizedError < ApiError #:nodoc: 38 | def initialize(s = 'unauthroized', code = 401) 39 | super s, code 40 | end 41 | end 42 | 43 | class PartialNotFoundError < ApiError #:nodoc: 44 | def initialize(s = '"partial"') 45 | super "partial #{s} not found", 404 46 | end 47 | end 48 | end 49 | 50 | class CustomError < Ki::ApiError 51 | end 52 | -------------------------------------------------------------------------------- /lib/ki/utils/descendants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | module Descendants 5 | def descendants 6 | ObjectSpace.each_object(Class).select { |klass| klass < self } 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/ki/utils/extra_irb.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # http://jasonroelofs.com/2009/04/02/embedding-irb-into-your-ruby-application/ 4 | require 'irb' 5 | 6 | module IRB # :nodoc: 7 | def self.start_session(binding) 8 | unless @__initialized 9 | args = ARGV 10 | ARGV.replace(ARGV.dup) 11 | IRB.setup(nil) 12 | ARGV.replace(args) 13 | @__initialized = true 14 | end 15 | 16 | workspace = WorkSpace.new(binding) 17 | 18 | irb = Irb.new(workspace) 19 | 20 | @CONF[:IRB_RC]&.call(irb.context) 21 | @CONF[:MAIN_CONTEXT] = irb.context 22 | 23 | catch(:IRB_EXIT) do 24 | irb.eval_input 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/ki/utils/extra_ruby.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class String 4 | # Converts a string to a class 5 | # 6 | # ==== Examples 7 | # 8 | # class User 9 | # end 10 | # 11 | # "user".to_class == User 12 | # 13 | def to_class 14 | chain = split '::' 15 | klass = Kernel 16 | chain.each do |klass_string| 17 | klass = klass.const_get klass_string.split('_').map(&:capitalize).join('') 18 | end 19 | klass.is_a?(Class) ? klass : nil 20 | rescue NameError 21 | nil 22 | end 23 | end 24 | 25 | class Array 26 | def stringify_ids 27 | collect do |e| 28 | if e['_id'] 29 | e['id'] = e['_id'].to_s 30 | e.delete('_id') 31 | end 32 | if e[:_id] 33 | e['id'] = e[:_id].to_s 34 | e.delete(:_id) 35 | end 36 | e 37 | end 38 | end 39 | 40 | def present? 41 | !nil? && !empty? 42 | end 43 | end 44 | 45 | class NilClass 46 | def present? 47 | false 48 | end 49 | end 50 | 51 | class Object 52 | def try(*a, &b) 53 | if a.empty? && block_given? 54 | yield self 55 | else 56 | __send__(*a, &b) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/ki/utils/indifferent_hash.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class IndifferentHash < Hash 4 | def []=(key, val) 5 | key = key.to_sym 6 | super(key, val) 7 | end 8 | 9 | def [](*args) 10 | args[0] = args[0].to_sym 11 | super(*args) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/ki/utils/logger.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ::Logger 4 | alias write << 5 | end 6 | -------------------------------------------------------------------------------- /lib/ki/utils/redirect_to_helper.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/lib/ki/utils/redirect_to_helper.rb -------------------------------------------------------------------------------- /lib/ki/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Ki 4 | VERSION = '0.4.12' 5 | end 6 | -------------------------------------------------------------------------------- /lib/ki/views/404.haml: -------------------------------------------------------------------------------- 1 | %h1 404 2 | -------------------------------------------------------------------------------- /lib/ki/views/instadmin.haml: -------------------------------------------------------------------------------- 1 | = js "//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js" 2 | 3 | %div{ 'ng-app' => 'instaAdminApp', 'ng-controller' => 'InstaAdmin' } 4 | %h1 Ki InstAdmin 5 | 6 | %p 7 | Connected to 8 | = Ki::Orm::Db.instance.connection_string 9 | 10 | %input{ 'ng-model' => 'verb' } 11 | %input{ 'ng-model' => 'resource' } 12 | %button{ 'ng-click' => 'executeReq()' } Execute Request 13 | 14 | %textarea{ 'ng-model' => 'input' } 15 | %textarea{ 'ng-model' => 'output' } 16 | 17 | :css 18 | textarea { 19 | width: 100%; 20 | height: 300px; 21 | padding: 15px; 22 | } 23 | 24 | button, input { 25 | width: 33%; 26 | padding: 15px; 27 | } 28 | 29 | :javascript 30 | var adminApp = angular.module('instaAdminApp', []); 31 | 32 | adminApp.controller('InstaAdmin', function ($scope, $http) { 33 | $scope.verb = 'get'; 34 | $scope.resource = 'users'; 35 | $scope.input = '{}' 36 | 37 | $scope.executeReq = function () { 38 | $http[$scope.verb]("/" + $scope.resource + ".json", JSON.parse($scope.input)). 39 | success(function(data, status, headers, config) { 40 | console.log(data); 41 | $scope.output = JSON.stringify(data, `undefined`, 2) 42 | }). 43 | error(function(data, status, headers, config) { 44 | console.log(data); 45 | $scope.output = JSON.stringify(data, `undefined`, 2) 46 | }); 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /lib/ki/views/instadoc.haml: -------------------------------------------------------------------------------- 1 | %h1 Ki InstaDoc 2 | 3 | %ul 4 | - Ki::Model.descendants.each do |model| 5 | %li 6 | %div 7 | %h2= model.to_s 8 | - unless model.annotations.nil? 9 | - unless model.annotations[:doc].nil? 10 | %p= model.annotations[:doc][:desc] 11 | %sub 12 | Endpoint: 13 | = "/#{model.to_s.downcase}.json" 14 | -# TODO: if any attributes 15 | %h3 Attributes 16 | - [ { items: model.required_attributes, key: :required_attributes }, { items: model.forbidden_actions, key: :forbidden_actions }, { items: model.unique_attributes, key: :unique_attributes }].each do |array| 17 | - if array[:items].any? 18 | %p 19 | %b= array[:key] 20 | - array[:items].each do |item| 21 | %p 22 | = item 23 | - unless model.annotations.nil? 24 | - unless model.annotations[array[:key]].nil? 25 | %i= model.annotations[array[:key]][item] 26 | -# TODO: if any callbacks 27 | %h3 Callbacks 28 | - Ki::Model::Callbacks.public_instance_methods.each do |pim| 29 | - unless model.annotations.nil? 30 | - unless model.annotations[pim].nil? 31 | %b= pim 32 | - model.annotations[pim].keys.each do |pim_key| 33 | %p 34 | = pim_key 35 | %i= model.annotations[pim][pim_key] 36 | -# %p{style: 'color: gray'} 37 | -# = model.annotations 38 | %li 39 | %div 40 | %h2 Errors 41 | %h3 Api Errors 42 | %ul 43 | %li 44 | = Ki::ApiError 45 | - Ki::ApiError.descendants.each do |error| 46 | %li 47 | = error 48 | %h3 Custom Errors 49 | %ul 50 | - CustomError.descendants.each do |error| 51 | %li 52 | = error 53 | 54 | %h3 Sample response 55 | %pre 56 | = Ki::ApiError.new('Bad request').result.to_json 57 | -------------------------------------------------------------------------------- /spec/config.yml.example: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: np_development 4 | host: 127.0.0.1 5 | port: 27017 6 | 7 | test: 8 | middleware: [ 9 | 'ApiHandler', 10 | 'InstaDoc', 11 | 'AdminInterfaceGenerator', 12 | 'CoffeeCompiler', 13 | 'SassCompiler', 14 | 'HamlCompiler', 15 | 'PublicFileServer' 16 | ] 17 | database: 18 | name: np_test 19 | host: 127.0.0.1 20 | port: 27017 21 | 22 | production: 23 | database: 24 | name: np 25 | host: 127.0.0.1 26 | port: 27017 27 | -------------------------------------------------------------------------------- /spec/examples/base/.ruby-gemset: -------------------------------------------------------------------------------- 1 | ki 2 | -------------------------------------------------------------------------------- /spec/examples/base/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.2 2 | -------------------------------------------------------------------------------- /spec/examples/base/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'ki' 4 | -------------------------------------------------------------------------------- /spec/examples/base/app.rb: -------------------------------------------------------------------------------- 1 | require 'ki' 2 | 3 | # class Message < Ki::Model 4 | # requires :title 5 | # forbid :delete 6 | # end 7 | -------------------------------------------------------------------------------- /spec/examples/base/client.rb: -------------------------------------------------------------------------------- 1 | require 'faye/websocket' 2 | require 'eventmachine' 3 | 4 | EM.run { 5 | ws = Faye::WebSocket::Client.new('ws://localhost:1337/faye') 6 | 7 | ws.on :open do |event| 8 | p [:open] 9 | ws.send('Hello, world!') 10 | end 11 | 12 | ws.on :message do |event| 13 | p [:message, event.data] 14 | end 15 | 16 | ws.on :close do |event| 17 | p [:close, event.code, event.reason] 18 | ws = nil 19 | end 20 | } 21 | -------------------------------------------------------------------------------- /spec/examples/base/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | use Rack::Reloader, 0 3 | run Ki::Ki.new 4 | -------------------------------------------------------------------------------- /spec/examples/base/config.yml: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: np_development 4 | host: 127.0.0.1 5 | port: 27017 6 | 7 | test: 8 | database: 9 | name: np_test 10 | host: 127.0.0.1 11 | port: 27017 12 | 13 | production: 14 | database: 15 | name: np 16 | host: 127.0.0.1 17 | port: 27017 18 | -------------------------------------------------------------------------------- /spec/examples/base/logs/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/base/logs/.keep -------------------------------------------------------------------------------- /spec/examples/base/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/base/public/favicon.ico -------------------------------------------------------------------------------- /spec/examples/base/public/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/base/public/javascripts/.gitkeep -------------------------------------------------------------------------------- /spec/examples/base/public/javascripts/app.js: -------------------------------------------------------------------------------- 1 | // var client = new Faye.Client('/faye'); 2 | // client.disable('autodisconnect'); 3 | // client.publish('/message', {text: 'Hi there'}); 4 | // var subscription = client.subscribe('/message', function(message) { 5 | // console.log(message); 6 | // }).then(function () { 7 | // console.log('active'); 8 | // }); 9 | 10 | var socket = new WebSocket("ws://localhost:1337/faye"); 11 | socket.onmessage = function(m) {console.log(m);} 12 | socket.onclose = function() {console.log('socket closed');} 13 | -------------------------------------------------------------------------------- /spec/examples/base/public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/base/public/stylesheets/.gitkeep -------------------------------------------------------------------------------- /spec/examples/base/views/index.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Ki Framework 5 | %body 6 | %p Hello World 7 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/.ruby-gemset: -------------------------------------------------------------------------------- 1 | couch-lock 2 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.2 2 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'ki', :path => '../../..' 4 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | use Rack::Reloader, 0 3 | run Ki::Ki.new 4 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/config.yml: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: couch-lock_development 4 | host: 127.0.0.1 5 | port: 27017 6 | 7 | test: 8 | database: 9 | name: couch-lock_test 10 | host: 127.0.0.1 11 | port: 27017 12 | 13 | production: 14 | database: 15 | name: couch-lock 16 | host: 127.0.0.1 17 | port: 27017 18 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sudo apt-get install disper vlc 4 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/doorbell-1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/doorbell-1.wav -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/favicon.ico -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/javascripts/.gitkeep -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/javascripts/clApp.coffee: -------------------------------------------------------------------------------- 1 | clApp = angular.module("clApp", []) 2 | 3 | clApp.directive "autoFocus", ($timeout) -> 4 | restrict: "AC" 5 | link: (_scope, _element) -> 6 | $timeout (-> 7 | _element[0].focus() 8 | return 9 | ), 0 10 | return 11 | 12 | clApp.controller "WikiController", ($scope, $http) -> 13 | $scope.refresh = -> 14 | $http.get("/wiki.json?base=1").success((data, status, headers, config) -> 15 | $scope.wiki = data[0] 16 | ).error (data, status, headers, config) -> 17 | console.log data 18 | $scope.refresh() 19 | 20 | $scope.$watch 'wiki.body', (newValue, oldValue) -> 21 | return unless oldValue? 22 | $http.put("/wiki.json", $scope.wiki); 23 | 24 | $scope.$watch 'wiki.title', (newValue, oldValue) -> 25 | return unless oldValue? 26 | $http.put("/wiki.json", $scope.wiki); 27 | 28 | clApp.controller "ShopController", ($scope, $http) -> 29 | $scope.shopping_items = [] 30 | $scope.item = { 31 | name: "" 32 | qty: 1 33 | } 34 | 35 | $scope.refresh = -> 36 | $http.get("/items.json").success((data, status, headers, config) -> 37 | $scope.shopping_items = data 38 | ).error (data, status, headers, config) -> 39 | console.log data 40 | $scope.refresh() 41 | 42 | $scope.update= (item) -> 43 | $http.put("/items.json", item 44 | ).success((data, status, headers, config) -> 45 | $scope.refresh() 46 | ).error (data, status, headers, config) -> 47 | console.log data 48 | 49 | $scope.create= -> 50 | if $scope.item.name? and $scope.item.qty? and $scope.item.name != '' 51 | $http.post("/items.json", { 52 | name: $scope.item.name 53 | qty: $scope.item.qty 54 | }).success((data, status, headers, config) -> 55 | $scope.item = { 56 | name: "" 57 | qty: 1 58 | } 59 | $scope.refresh() 60 | ).error (data, status, headers, config) -> 61 | console.log data 62 | 63 | $scope.delete= (id) -> 64 | $http.delete("/items.json?id=#{id}", 65 | ).success((data, status, headers, config) -> 66 | $scope.refresh() 67 | ).error (data, status, headers, config) -> 68 | console.log data 69 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/couch-lock/public/stylesheets/.gitkeep -------------------------------------------------------------------------------- /spec/examples/couch-lock/public/stylesheets/main.sass: -------------------------------------------------------------------------------- 1 | .row 2 | margin-top: 10px 3 | .no-padding 4 | div 5 | padding-left: 5px 6 | padding-right: 5px 7 | 8 | .menu 9 | a 10 | min-width: 100% 11 | 12 | .rowz 13 | margin-top: 10px 14 | text-align: center 15 | a 16 | vertical-align: middle 17 | min-width: 200px 18 | max-width: 300px 19 | input[type=number] 20 | max-width: 70px 21 | text-align: center 22 | 23 | .zero_border 24 | width: 100% 25 | border-style: none 26 | border-color: Transparent 27 | overflow: auto 28 | outline: none 29 | text-align: justify 30 | 31 | input 32 | @extend .zero_border 33 | 34 | textarea 35 | height: 420px 36 | @extend .zero_border 37 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/views/index.haml: -------------------------------------------------------------------------------- 1 | .row.text-center.menu 2 | .col-md-4.col-md-offset-4.col-xs-12 3 | %a.btn.btn-primary{ href: '/shop' } 4 | %i.fa.fa-shopping-cart 5 | shopping list 6 | .row.text-center.menu 7 | .col-md-4.col-md-offset-4.col-xs-12 8 | %a.btn.btn-primary{ href: '/wiki' } 9 | %i.fa.fa-krw 10 | iki 11 | .row.text-center.menu 12 | .col-md-4.col-md-offset-4.col-xs-12 13 | %a.btn.btn-primary{ href: '/ring.json?redirect_to=/' } 14 | %i.fa.fa-bell 15 | ring 16 | .row.text-center.menu 17 | .col-md-4.col-md-offset-4.col-xs-12 18 | - unless vlc_running? 19 | %a.btn.btn-primary{ href: '/fireplace.json?q=start&redirect_to=/' } 20 | %i.fa.fa-fire 21 | fireplace 22 | - else 23 | %a.btn.btn-primary{ href: '/fireplace.json?q=kill&redirect_to=/' } 24 | %i.fa.fa-fire-extinguisher 25 | kill fireplace 26 | .row.text-center.menu 27 | .col-md-4.col-md-offset-4.col-xs-12 28 | - unless couch_lock_running? 29 | %a.btn.btn-primary{ href: '/jrobot.json?q=listen&redirect_to=/' } 30 | %i.fa.fa-coffee 31 | couch-lock 32 | - else 33 | %a.btn.btn-primary{ href: '/jrobot.json?q=kill&redirect_to=/' } 34 | %i.fa.fa-times 35 | kill couch-lock 36 | .row.text-center.menu 37 | .col-md-4.col-md-offset-4.col-xs-12 38 | %a.btn.btn-primary{ href: 'couchlock://192.168.0.12:9876' } 39 | %i.fa.fa-android 40 | android 41 | .row.text-center.menu 42 | .col-md-4.col-md-offset-4.col-xs-12 43 | - unless vlc_running? 44 | %a.btn.btn-primary{ href: '/radio.json?q=rockfm&redirect_to=/' } 45 | %i.fa.fa-headphones 46 | radio now 47 | - else 48 | %a.btn.btn-primary{ href: '/fireplace.json?q=kill&redirect_to=/' } 49 | %i.fa.fa-headphones 50 | kill radio 51 | .row.text-center.menu 52 | .col-md-2.col-md-offset-4.col-xs-6 53 | %a.btn.btn-primary{ href: '/volume.json?q=down&redirect_to=/' } 54 | %i.fa.fa-volume-down 55 | .col-md-2.col-xs-6 56 | %a.btn.btn-primary{ href: '/volume.json?q=up&redirect_to=/' } 57 | %i.fa.fa-volume-up 58 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/views/layout.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html{ "ng-app" => "clApp" } 3 | %head 4 | %title Ki Framework - Couch Lock Remote Control 5 | 6 | %meta{ name: "viewport", content: "width=device-width, initial-scale=1" } 7 | 8 | %link{href: "stylesheets/font-awesome.min.css", rel: "stylesheet"}/ 9 | %link{href: "stylesheets/bootstrap.min.css", rel: "stylesheet"}/ 10 | %link{href: "stylesheets/bootstrap-theme.min.css", rel: "stylesheet"}/ 11 | %link{href: "stylesheets/main.css", rel: "stylesheet"}/ 12 | %body 13 | .container 14 | .row 15 | .col-md-4.col-md-offset-4.col-xs-12 16 | %a.btn.btn-primary{ href: '/' } 17 | %i.fa.fa-home 18 | %a.btn.btn-primary{ href: 'javascript: location.reload()' } 19 | %i.fa.fa-refresh 20 | %a.pull-right.btn.btn-primary{ href: '/settings' } 21 | %i.fa.fa-cog 22 | = yield 23 | 24 | %script{src: "javascripts/angular.min.js"} 25 | %script{src: "javascripts/clApp.js"} 26 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/views/settings.haml: -------------------------------------------------------------------------------- 1 | .row.text-center.menu 2 | .col-md-4.col-md-offset-4.col-xs-12 3 | - if display_status == 'cloned' 4 | %a.btn.btn-primary{ href: '/monitors.json?q=e&redirect_to=/settings' } 5 | %i.fa.fa-desktop 6 | %i.fa.fa-desktop 7 | extend 8 | - else 9 | %a.btn.btn-primary{ href: '/monitors.json?q=c&redirect_to=/settings' } 10 | %i.fa.fa-desktop 11 | clone 12 | .row.text-center.menu 13 | .col-md-4.col-md-offset-4.col-xs-12 14 | - if sound_output == 'hdmi' 15 | %a.btn.btn-primary{ href: '/sound.json?q=speakers&redirect_to=/settings' } 16 | %i.fa.fa-file-audio-o 17 | speakers 18 | - else 19 | %a.btn.btn-primary{ href: '/sound.json?q=hdmi&redirect_to=/settings' } 20 | %i.fa.fa-file-audio-o 21 | hdmi 22 | .row.text-center.menu 23 | .col-md-4.col-md-offset-4.col-xs-12 24 | %a.btn.btn-primary{ href: '/volume.json?q=manager&redirect_to=/settings' } 25 | %i.fa.fa-volume-up 26 | gnome volume manager 27 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/views/shop.haml: -------------------------------------------------------------------------------- 1 | .row{ "ng-controller" => "ShopController" } 2 | .col-md-4.col-md-offset-4.col-xs-12 3 | .row.no-padding 4 | .col-xs-7 5 | %input.form-control{ type: "text", placeholder: "name", "ng-model" => "item.name" } 6 | .col-xs-3 7 | %input.form-control{ type: "number", placeholder: "Qty", min: 0, "ng-model" => "item.qty" } 8 | .col-xs-2 9 | %a.pull-right.btn.btn-primary{ "ng-click" => "create()"} 10 | %i.fa.fa-plus 11 | 12 | .col-xs-12 13 | %hr 14 | 15 | .col-md-4.col-md-offset-4.col-xs-12{ "ng-repeat" => "si in shopping_items" } 16 | .row.no-padding 17 | .col-xs-7 18 | %input.form-control{ type: "text", "ng-model" => "si.name", "ng-blur" => "update(si)"} 19 | .col-xs-3 20 | %input.form-control{ type: "number", placeholder: "Qty", min: 0, "ng-model" => "si.qty", "ng-blur" => "update(si)" } 21 | .col-xs-2 22 | %a.btn.btn-primary{ "ng-click" => "delete(si.id)" } 23 | %i.fa.fa-trash 24 | -------------------------------------------------------------------------------- /spec/examples/couch-lock/views/wiki.haml: -------------------------------------------------------------------------------- 1 | - wiki_defaults 2 | .row{ "ng-controller" => "WikiController" } 3 | .col-md-4.col-md-offset-4.col-xs-12 4 | %p.text-center 5 | %input{ "type" => "text", "ng-model" => "wiki.title", "ng-model-options" => "{debounce: 1000}" } 6 | %textarea{ "auto-focus" => true, "ng-model" => "wiki.body", "ng-model-options" => "{debounce: 1000}" } 7 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/components/" 3 | } 4 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/.ruby-gemset: -------------------------------------------------------------------------------- 1 | json.northpole.ro 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.3 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/Capfile: -------------------------------------------------------------------------------- 1 | # Load DSL and Setup Up Stages 2 | require 'capistrano/setup' 3 | 4 | # Includes default deployment tasks 5 | require 'capistrano/deploy' 6 | require 'capistrano/scm/git' 7 | install_plugin Capistrano::SCM::Git 8 | 9 | # Includes tasks from other gems included in your Gemfile 10 | # 11 | # For documentation on these, see for example: 12 | # 13 | # https://github.com/capistrano/rvm 14 | # https://github.com/capistrano/rbenv 15 | # https://github.com/capistrano/chruby 16 | # https://github.com/capistrano/bundler 17 | # https://github.com/capistrano/rails 18 | # 19 | require 'capistrano/rvm' 20 | # require 'capistrano/rbenv' 21 | # require 'capistrano/chruby' 22 | # require 'capistrano/bundler' 23 | # require 'capistrano/rails/assets' 24 | # require 'capistrano/rails/migrations' 25 | 26 | # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. 27 | # Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r } 28 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'ki', :path => '../../..' 4 | gem 'passenger', '5.3.3' 5 | gem 'thin' 6 | gem 'capistrano' 7 | gem 'capistrano-rvm' 8 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../../.. 3 | specs: 4 | ki (0.4.12) 5 | bson_ext 6 | bundler 7 | coffee-script 8 | faye-websocket 9 | haml 10 | mongo 11 | rack 12 | rack-cors 13 | rack-parser 14 | sass 15 | thor 16 | 17 | GEM 18 | remote: https://rubygems.org/ 19 | specs: 20 | airbrussh (1.3.0) 21 | sshkit (>= 1.6.1, != 1.7.0) 22 | bson (4.3.0) 23 | bson_ext (1.5.1) 24 | capistrano (3.10.1) 25 | airbrussh (>= 1.0.0) 26 | i18n 27 | rake (>= 10.0.0) 28 | sshkit (>= 1.9.0) 29 | capistrano-rvm (0.1.2) 30 | capistrano (~> 3.0) 31 | sshkit (~> 1.2) 32 | coffee-script (2.4.1) 33 | coffee-script-source 34 | execjs 35 | coffee-script-source (1.12.2) 36 | concurrent-ruby (1.0.5) 37 | daemons (1.2.6) 38 | eventmachine (1.2.5) 39 | execjs (2.7.0) 40 | faye-websocket (0.10.7) 41 | eventmachine (>= 0.12.0) 42 | websocket-driver (>= 0.5.1) 43 | ffi (1.9.25) 44 | haml (5.0.4) 45 | temple (>= 0.8.0) 46 | tilt 47 | i18n (1.0.0) 48 | concurrent-ruby (~> 1.0) 49 | mongo (2.6.0) 50 | bson (>= 4.3.0, < 5.0.0) 51 | net-scp (1.2.1) 52 | net-ssh (>= 2.6.5) 53 | net-ssh (4.2.0) 54 | passenger (5.3.3) 55 | rack 56 | rake (>= 0.8.1) 57 | rack (1.6.9) 58 | rack-cors (1.0.2) 59 | rack-parser (0.7.0) 60 | rack 61 | rake (12.3.0) 62 | rb-fsevent (0.10.3) 63 | rb-inotify (0.9.10) 64 | ffi (>= 0.5.0, < 2) 65 | sass (3.5.6) 66 | sass-listen (~> 4.0.0) 67 | sass-listen (4.0.0) 68 | rb-fsevent (~> 0.9, >= 0.9.4) 69 | rb-inotify (~> 0.9, >= 0.9.7) 70 | sshkit (1.16.0) 71 | net-scp (>= 1.1.2) 72 | net-ssh (>= 2.8.0) 73 | temple (0.8.0) 74 | thin (1.7.2) 75 | daemons (~> 1.0, >= 1.0.9) 76 | eventmachine (~> 1.0, >= 1.0.4) 77 | rack (>= 1, < 3) 78 | thor (0.20.0) 79 | tilt (2.0.8) 80 | websocket-driver (0.7.0) 81 | websocket-extensions (>= 0.1.0) 82 | websocket-extensions (0.1.3) 83 | 84 | PLATFORMS 85 | ruby 86 | 87 | DEPENDENCIES 88 | capistrano 89 | capistrano-rvm 90 | ki! 91 | passenger (= 5.3.3) 92 | thin 93 | 94 | BUNDLED WITH 95 | 1.16.1 96 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/app.rb: -------------------------------------------------------------------------------- 1 | require 'ki' 2 | 3 | NP_API_KEY = 'api_key' 4 | NP_SECRET = 'secret' 5 | NP_GUEST_API_KEY = 'guest' 6 | NP_GUEST_SECRET = 'guest' 7 | NP_URL = 'http://json.northpole.ro' 8 | 9 | def get_time 10 | Time.now.to_i 11 | end 12 | 13 | module Ki 14 | module Helpers 15 | def other_developer_count 16 | user_count = haml("%span.badge #{User.count}") 17 | storage_count = haml("%span.badge #{Storage.count}") 18 | "#{user_count} developers with #{storage_count} resources" 19 | end 20 | end 21 | 22 | class Model 23 | def before_all 24 | ensure_authorization 25 | end 26 | 27 | private 28 | 29 | def ensure_authorization 30 | if params[NP_API_KEY].nil? 31 | raise UnauthorizedError.new("not authorized. #{NP_API_KEY} is missing") 32 | end 33 | if params[NP_SECRET].nil? 34 | raise UnauthorizedError.new("not authorized. #{NP_SECRET} is missing") 35 | end 36 | 37 | h = { NP_API_KEY => params[NP_API_KEY], NP_SECRET => params[NP_SECRET]} 38 | u = User.find(h) 39 | unless action == :create && self.class == User 40 | if u.empty? 41 | raise UnauthorizedError.new 42 | end 43 | end 44 | params.delete(NP_SECRET) if !required_attributes.include? NP_SECRET.to_sym 45 | end 46 | end 47 | end 48 | 49 | class User < Ki::Model 50 | requires NP_API_KEY.to_sym, NP_SECRET.to_sym 51 | forbid :delete, :update 52 | unique NP_API_KEY.to_sym 53 | 54 | def before_create 55 | raise Ki::ApiError.new('invalid api key. /(\W)/ only', 400) unless valid_api_key?(params[NP_API_KEY]) 56 | params['created_at'] = get_time 57 | end 58 | 59 | private 60 | 61 | def valid_api_key? s 62 | s.gsub(/(\W|\d)/, "") == s 63 | end 64 | end 65 | 66 | class Storage < Ki::Model 67 | requires NP_API_KEY.to_sym 68 | 69 | def before_create 70 | params['created_at'] = get_time 71 | end 72 | 73 | def before_update 74 | raise Ki::ApiError.new('id param required', 400) unless params['id'] 75 | storage = Storage.find(params['id']) 76 | raise Ki::ApiError.new('json object not found', 404) if storage.empty? 77 | params['created_at'] = storage.first['created_at'] 78 | end 79 | end 80 | 81 | class WriteOnlyStorage < Storage 82 | forbid :delete, :update 83 | end 84 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json.northpole.ro", 3 | "version": "0.4.10", 4 | "homepage": "https://github.com/mess110/ki", 5 | "authors": [ 6 | "Cristian Mircea Messel " 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "public/components/", 14 | "test", 15 | "tests" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/client.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | 3 | class NPClient 4 | include HTTParty 5 | base_uri 'localhost:9292' 6 | format :json 7 | 8 | def options 9 | { 10 | body: { 11 | secret: 'foo' 12 | } 13 | } 14 | end 15 | 16 | def find o 17 | self.class.get("/storage.json", body: o) 18 | end 19 | 20 | def create o 21 | self.class.post("/storage.json", body: o) 22 | end 23 | end 24 | 25 | p NPClient.new.find({"api_key" => "guest", "secret" => "secret"}) 26 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | unless `hostname`.include?('glassic-jenkins') 3 | Faye::WebSocket.load_adapter('thin') 4 | end 5 | use Rack::Reloader, 0 6 | run Ki::Ki.new 7 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/config.yml: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: np_development 4 | host: 127.0.0.1 5 | # host: 172.17.0.3 6 | port: 27017 7 | add_middleware: 'Realtime' 8 | 9 | test: 10 | database: 11 | name: np_test 12 | host: 127.0.0.1 13 | port: 27017 14 | 15 | production: 16 | database: 17 | name: np 18 | host: 127.0.0.1 19 | # host: 172.17.0.3 20 | port: 27017 21 | add_middleware: 'Realtime' 22 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/config.yml.backup: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: np_development 4 | host: 172.17.0.3 5 | port: 27017 6 | 7 | test: 8 | database: 9 | name: np_test 10 | host: 127.0.0.1 11 | port: 27017 12 | 13 | production: 14 | database: 15 | name: np 16 | host: 127.0.0.1 17 | port: 27017 18 | add_middleware: 'Realtime' 19 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/config/deploy.rb: -------------------------------------------------------------------------------- 1 | # config valid only for Capistrano 3.1 2 | lock '3.10.1' 3 | 4 | set :application, 'json.northpole.ro' 5 | set :repo_url, 'git@github.com:mess110/ki.git' 6 | 7 | # Default branch is :master 8 | # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp } 9 | 10 | # Default deploy_to directory is /var/www/my_app 11 | set :deploy_to, '/home/kiki/json.northpole.ro' 12 | 13 | # Default value for :scm is :git 14 | # set :scm, :git 15 | 16 | # Default value for :format is :pretty 17 | # set :format, :pretty 18 | 19 | # Default value for :log_level is :debug 20 | # set :log_level, :debug 21 | 22 | # Default value for :pty is false 23 | # set :pty, true 24 | 25 | # Default value for :linked_files is [] 26 | # set :linked_files, %w{config/database.yml} 27 | 28 | # Default value for linked_dirs is [] 29 | # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system} 30 | set :linked_dirs, %w{tmp} 31 | 32 | # Default value for default_env is {} 33 | # set :default_env, { path: "/opt/ruby/bin:$PATH" } 34 | set :rvm_ruby_version, '2.4.3@json.northpole.ro' 35 | 36 | # Default value for keep_releases is 5 37 | # set :keep_releases, 5 38 | 39 | namespace :deploy do 40 | 41 | desc 'Restart application' 42 | task :restart do 43 | on roles(:app), in: :sequence, wait: 5 do 44 | # Your restart mechanism here, for example: 45 | execute :touch, release_path.join('tmp/restart.txt') 46 | # run "#{sudo} service nginx #{command}" 47 | end 48 | end 49 | 50 | after :publishing, :restart 51 | 52 | after :restart, :clear_cache do 53 | on roles(:web), in: :groups, limit: 3, wait: 10 do 54 | # Here we can do anything such as: 55 | # within release_path do 56 | # execute :rake, 'cache:clear' 57 | # end 58 | end 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | role :app, %w{mace@glassic.northpole.ro} 2 | role :web, %w{mace@glassic.northpole.ro} 3 | role :db, %w{mace@glassic.northpole.ro} 4 | 5 | set :deploy_to, '/home/mace/json.northpole.ro' 6 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/foo.rb: -------------------------------------------------------------------------------- 1 | require 'uri' 2 | require 'net/http' 3 | require 'json' 4 | 5 | uri = URI.parse("http://localhost:9292") 6 | http = Net::HTTP.new(uri.host, uri.port) 7 | request = Net::HTTP::Post.new("/storage.json") 8 | request.body = {'api_key' => 'guest', 'secret' => 'guest'}.to_json 9 | response = http.request(request) 10 | p response.body 11 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/JNorthPole.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/JNorthPole.jar -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/.gitignore: -------------------------------------------------------------------------------- 1 | logs/* 2 | !.gitkeep 3 | node_modules/ 4 | bower_components/ 5 | tmp 6 | .DS_Store 7 | .idea 8 | app/jspm_packages 9 | build/ 10 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globalstrict": true, 3 | "globals": { 4 | "angular": false, 5 | "describe": false, 6 | "it": false, 7 | "expect": false, 8 | "beforeEach": false, 9 | "afterEach": false, 10 | "module": false, 11 | "inject": false 12 | } 13 | } -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | 5 | before_script: 6 | - export DISPLAY=:99.0 7 | - sh -e /etc/init.d/xvfb start 8 | - npm start > /dev/null & 9 | - npm run update-webdriver 10 | - sleep 1 # give server time to start 11 | 12 | script: 13 | - node_modules/.bin/karma start karma.conf.js --no-auto-watch --single-run --reporters=dots --browsers=Firefox 14 | - node_modules/.bin/protractor e2e-tests/protractor.conf.js --browser=firefox 15 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2014 Google, Inc. http://angularjs.org 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/README.md: -------------------------------------------------------------------------------- 1 | # Angular Material-Start 2 | 3 | This is a template project. It customizes [this](https://github.com/angular/material-start). 4 | 5 | Extra features 6 | 7 | * updated angular-material version 8 | * gulp 9 | * coffee-script 10 | 11 | ## Getting Started 12 | 13 | #### Clone material-start 14 | 15 | ```bash 16 | git clone --depth=1 https://github.com/mess110/material-start.git 17 | ``` 18 | 19 | #### Install Dependencies 20 | 21 | ``` 22 | npm install 23 | ``` 24 | 25 | ### Run End-to-End Tests 26 | 27 | To run your e2e tests your should install and configure Protractor and the Selenium WebServer. 28 | These are already specified as npm dependencies within `package.json`. Simply run these 29 | terminal commands: 30 | 31 | ```console 32 | npm update 33 | webdriver-manager update 34 | ``` 35 | 36 | Your can read more details about Protractor and e2e here: http://angular.github.io/protractor/#/ 37 | for more details on Protractor. 38 | 39 | 1. Start your local HTTP Webserver: `live-server` or `http-server`. 40 | 41 | ```console 42 | cd ./app; live-server; 43 | ``` 44 | 45 | > Note: since `live-server` is working on port 8080, we configure the `protractor.conf.js` to use 46 | `baseUrl: 'http://localhost:8080'` 47 | 48 | 2. In another tab, start a Webdriver instance: 49 | 50 | ```console 51 | webdriver-manager start 52 | ``` 53 | 54 | >This will start up a Selenium Server and will output a bunch of info logs. Your Protractor test 55 | will send requests to this server to control a local browser. You can see information about the 56 | status of the server at `http://localhost:4444/wd/hub`. If you see errors, verify path in 57 | `e2e-tests/protractor.conf.js` for `chromeDriver` and `seleniumServerJar` to your local file system. 58 | 59 | 3. Run your e2e tests using the `test` script defined in `package.json`: 60 | 61 | ```console 62 | npm test 63 | ``` 64 | 65 | > This uses the local **Protractor** installed at `./node_modules/protractor` 66 | 67 | ## Directory Layout 68 | 69 | ``` 70 | build/ --> "compiled" version of the project 71 | src/ --> all of the source files for the application 72 | assets/ --> default assets 73 | directives/ --> directives 74 | users/ --> package for user features 75 | views/ --> user related views 76 | app.sass --> app sass file 77 | index.html --> app layout file (the main html template file of the app) 78 | e2e-tests/ --> end-to-end tests 79 | protractor-conf.js --> Protractor config file 80 | scenarios.js --> end-to-end scenarios to be run by Protractor 81 | karma.conf.js --> config file for running unit tests with Karma 82 | ``` 83 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-material-starter", 3 | "description": "A starter project for AngularJS Material with coffee-script", 4 | "version": "0.0.3", 5 | "homepage": "https://github.com/mess110/material-start", 6 | "license": "MIT", 7 | "private": true, 8 | "dependencies": { 9 | "angular": "^1.4.x", 10 | "angular-animate": "^1.4.x", 11 | "angular-aria": "^1.4.x", 12 | "angular-material": "^0.11.0", 13 | "angular-route": "^1.4.x", 14 | "material-design-icons": "~2.0.0", 15 | "ngstorage": "~0.3.9" 16 | }, 17 | "devDependencies": { 18 | "angular-material": "~0.11.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/avatar-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/avatar-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/contact_mail_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/google_plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/hangouts.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_adb_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_add_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_clear_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_delete_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_lock_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_music_note_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_note_add_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_person_add_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_settings_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_share_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/ic_view_list_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/assets/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Roboto', sans-serif; 3 | font-size: 14px; 4 | height: 100%; 5 | margin: 0px; 6 | padding: 0px; 7 | overflow: hidden; } 8 | 9 | ::-webkit-scrollbar { 10 | display: none; } 11 | 12 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 13 | display: none !important; } 14 | 15 | .boobs { 16 | background: url("//media.giphy.com/media/EFIwNN4wo9Tig/giphy.gif") no-repeat center center; } 17 | 18 | .menu { 19 | background-color: transparent; 20 | border: none; 21 | height: 38px; 22 | margin: 16px; 23 | width: 36px; } 24 | 25 | md-list .md-button { 26 | color: inherit; 27 | font-weight: 500; 28 | text-align: left; 29 | width: 100%; } 30 | 31 | .selected:hover { 32 | background-color: #FFCDD2 !important; } 33 | 34 | .selected { 35 | background-color: #FFEBEE; } 36 | 37 | md-sidenav md-list { 38 | padding: 0px 0px 8px 0px; } 39 | 40 | #content { 41 | overflow: hidden; } 42 | #content .md-button.contact { 43 | background-color: transparent; 44 | border: none; 45 | width: 48px; 46 | height: 48px; 47 | margin: 8px auto 16px 0; 48 | position: absolute; 49 | top: 10px; 50 | right: 40px; } 51 | #content md-icon.avatar { 52 | margin-top: 10px; } 53 | #content .md-button.contact > md-icon { 54 | fill: black; 55 | width: 36px; 56 | height: 36px; } 57 | 58 | md-button.menuBtn > md-icon { 59 | fill: white; 60 | width: 24px; 61 | height: 24px; } 62 | 63 | #content .md-button.contact:active > md-icon { 64 | background-color: #dadada; 65 | border-radius: 75%; 66 | padding: 4px; 67 | transition: all 100ms ease-out 30ms; } 68 | 69 | .content-wrapper { 70 | position: relative; } 71 | 72 | md-toolbar h1 { 73 | font-size: 1.250em; 74 | font-weight: 400; } 75 | 76 | h1.center-it { 77 | margin: auto; } 78 | 79 | .avatar { 80 | position: relative; 81 | width: 128px; 82 | height: 128px; 83 | border: 1px solid #ddd; 84 | border-radius: 50%; 85 | display: inline-block; 86 | overflow: hidden; 87 | margin: 0px; 88 | vertical-align: middle; 89 | zoom: 0.70; 90 | transform: translateZ(0); 91 | -webkit-transform: scale(0.7); 92 | -moz-transform: scale(0.7); } 93 | 94 | md-bottom-sheet md-icon { 95 | margin-right: 20px; } 96 | 97 | span.name { 98 | font-weight: bold; 99 | font-size: 1.1em; 100 | padding-left: 5px; } 101 | 102 | .small-bot { 103 | padding-top: 6px !important; 104 | padding-bottom: 2px !important; } 105 | 106 | .done { 107 | text-decoration: line-through; } 108 | 109 | form[name=userForm] md-input-container { 110 | padding-left: 12px; 111 | padding-right: 12px; } 112 | 113 | input { 114 | color: rgba(0, 0, 0, 0.87) !important; } 115 | 116 | /* you can also define the transition style 117 | * on the base class as well (.repeat-item) */ 118 | .repeat-item.ng-enter, .repeat-item.ng-leave { 119 | -webkit-transition: 0.5s linear all; 120 | transition: 0.5s linear all; } 121 | 122 | .repeat-item.ng-enter { 123 | opacity: 0; } 124 | 125 | .repeat-item.ng-leave { 126 | opacity: 1; } 127 | .repeat-item.ng-leave.ng-leave-active { 128 | opacity: 0; } 129 | 130 | .repeat-item.ng-enter.ng-enter-active { 131 | opacity: 1; } 132 | 133 | .delete { 134 | float: right; } 135 | 136 | label.add-new { 137 | white-space: nowrap; 138 | overflow: hidden; 139 | text-overflow: ellipsis; } 140 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/app.js: -------------------------------------------------------------------------------- 1 | var app; 2 | 3 | app = angular.module('app', ['ngRoute', 'ngStorage', 'ngMaterial', 'users']); 4 | 5 | app.constant("myConfig", { 6 | title: 'mango {fruit}' 7 | }); 8 | 9 | app.config([ 10 | '$mdThemingProvider', '$mdIconProvider', function($mdThemingProvider, $mdIconProvider) { 11 | $mdIconProvider.defaultIconSet('./assets/svg/avatars.svg', 128).icon('menu', './assets/svg/menu.svg', 24).icon('share', './assets/svg/share.svg', 24).icon('google_plus', './assets/svg/google_plus.svg', 512).icon('hangouts', './assets/svg/hangouts.svg', 512).icon('twitter', './assets/svg/twitter.svg', 512).icon('phone', './assets/svg/phone.svg', 512).icon('person_add', './assets/svg/ic_person_add_48px.svg', 48).icon('clear', './assets/svg/ic_clear_48px.svg', 48).icon('plus', './assets/svg/ic_add_48px.svg', 48).icon('cog', './assets/svg/ic_settings_48px.svg', 48).icon('delete', './assets/svg/ic_delete_48px.svg', 48).icon('lock', './assets/svg/ic_lock_48px.svg', 48).icon('share', './assets/svg/ic_share_48px.svg', 48).icon('music', './assets/svg/ic_music_note_48px.svg', 48).icon('note_add', './assets/svg/ic_note_add_48px.svg', 48).icon('fullscreen', './assets/svg/ic_fullscreen_48px.svg', 48).icon('fullscreen_exit', './assets/svg/ic_fullscreen_exit_48px.svg', 48); 12 | $mdThemingProvider.theme('default').primaryPalette('deep-orange').accentPalette('blue').warnPalette('red').backgroundPalette('grey'); 13 | } 14 | ]); 15 | 16 | app.config([ 17 | '$routeProvider', function($routeProvider) { 18 | $routeProvider.when('/tutorial', { 19 | templateUrl: 'js/tutorial/tutorial.html' 20 | }).when('/blobs', { 21 | templateUrl: 'js/blobs/blobs.html', 22 | controller: 'BlobsController' 23 | }).when('/blobs/:id', { 24 | templateUrl: 'js/blobs/blobs.html', 25 | controller: 'BlobsController' 26 | }).otherwise({ 27 | redirectTo: '/blobs' 28 | }); 29 | } 30 | ]); 31 | 32 | app.config([ 33 | '$localStorageProvider', function($localStorageProvider) { 34 | $localStorageProvider.setKeyPrefix('app'); 35 | } 36 | ]); 37 | 38 | app.run(function($rootScope, myConfig) { 39 | $rootScope.myConfig = myConfig; 40 | }); 41 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/blobs/blobs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 |
39 |

<< Swipe left for fortune

40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/tutorial/tutorial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Mango The Polar Bear helps you get your &!@^% together. 5 |

6 |

With client side encryption

7 |

Of impossible decryption,

8 |

Your ideas and your notes

9 |

Will floats your boats.

10 |
11 |

12 | Data is stored at the North Pole. 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/users/UserService.js: -------------------------------------------------------------------------------- 1 | var UserService; 2 | 3 | UserService = function($q, $localStorage) { 4 | var users; 5 | if ($localStorage.accounts == null) { 6 | $localStorage.accounts = []; 7 | } 8 | users = $localStorage.accounts; 9 | return { 10 | loadAllUsers: function() { 11 | return $q.when(users); 12 | } 13 | }; 14 | }; 15 | 16 | angular.module('users').service('userService', ['$q', '$localStorage', UserService]); 17 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/users/Users.js: -------------------------------------------------------------------------------- 1 | angular.module('users', ['ngMaterial']); 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/build/js/users/users.html: -------------------------------------------------------------------------------- 1 | 2 |

{{ul.selected.name}}

3 |

{{ul.selected.content}}

4 | 5 | 6 | Contact {{ ul.selected.name }} 7 | 8 | 9 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/e2e/pages/ContactUser.js: -------------------------------------------------------------------------------- 1 | var ContactUser = function() { 2 | 3 | this.load = function() { 4 | return browser.get("/#"); 5 | }; 6 | 7 | this.buttons = function() { 8 | // @Todo - remove sleep() when https://github.com/angular/protractor/issues/2154 9 | browser.sleep(500); 10 | 11 | return element.all(by.css('[ng-click="cp.submitContact(item)"]')); 12 | }; 13 | 14 | this.focusedButton = function() { 15 | return browser.driver.switchTo().activeElement(); 16 | }; 17 | 18 | 19 | }; 20 | 21 | module.exports = ContactUser; 22 | 23 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/e2e/pages/UserDetails.js: -------------------------------------------------------------------------------- 1 | var UserDetails = function() { 2 | this.load = function() { browser.get('/#'); }; 3 | 4 | this.contactUser = function() { 5 | $('button.contact').click(); 6 | }; 7 | 8 | }; 9 | 10 | module.exports = UserDetails; 11 | 12 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/e2e/pages/UserList.js: -------------------------------------------------------------------------------- 1 | var UserList = function() { 2 | this.loadAll = function() { 3 | browser.get('/#'); 4 | }; 5 | 6 | this.count = function() { 7 | return element.all(by.css('md-list-item')).count(); 8 | }; 9 | }; 10 | 11 | module.exports = UserList; 12 | 13 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | allScriptsTimeout: 11000, 3 | 4 | specs: [ 5 | './**/*.js' 6 | ], 7 | 8 | capabilities: { 9 | 'browserName': 'chrome' 10 | }, 11 | 12 | baseUrl: 'http://localhost:8080', 13 | 14 | seleniumAddress:'http://127.0.0.1:4444/wd/hub', 15 | 16 | framework: 'jasmine', 17 | 18 | plugins: [{ 19 | chromeA11YDevTools: true, 20 | path: '../node_modules/protractor/plugins/accessibility/index.js' 21 | }], 22 | 23 | jasmineNodeOpts: { 24 | defaultTimeoutInterval: 30000 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/e2e/scenarios/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var UserList = require('../pages/UserList.js'); 4 | var UserDetails = require('../pages/UserDetails.js'); 5 | var ContactUser = require('../pages/ContactUser.js'); 6 | 7 | describe('my app', function() { 8 | 9 | var users = new UserList(); 10 | var details = new UserDetails(); 11 | var contact = new ContactUser(); 12 | 13 | beforeEach(function() { 14 | users.loadAll(); 15 | }); 16 | 17 | it('should load a list of users', function() { 18 | expect(users.count()).toBeGreaterThan(1); 19 | }); 20 | 21 | describe('selecting a user', function() { 22 | 23 | beforeEach(function() { 24 | return details.contactUser(); 25 | }); 26 | 27 | it('should set focus on first button in the bottomsheet view', function() { 28 | expect( contact.buttons().count() ).toBe(4); 29 | expect( contact.buttons().first().getText() ).toEqual( 'PHONE' ); 30 | expect( contact.focusedButton().getText() ).toEqual( 'PHONE' ); 31 | }); 32 | 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config){ 2 | config.set({ 3 | 4 | basePath : './', 5 | 6 | files : [ 7 | 'app/bower_components/angular/angular.js', 8 | 'app/bower_components/angular-route/angular-route.js', 9 | 'app/bower_components/angular-aria/angular-aria.js', 10 | 'app/bower_components/angular-mocks/angular-mocks.js', 11 | 'app/bower_components/angular-material/angular-material.js', 12 | 'app/components/**/*.js', 13 | 'app/view*/**/*.js' 14 | ], 15 | 16 | autoWatch : true, 17 | 18 | frameworks: ['jasmine'], 19 | 20 | browsers : ['Chrome'], 21 | 22 | plugins : [ 23 | 'karma-chrome-launcher', 24 | 'karma-firefox-launcher', 25 | 'karma-jasmine', 26 | 'karma-junit-reporter' 27 | ], 28 | 29 | junitReporter : { 30 | outputFile: 'test_out/unit.xml', 31 | suite: 'unit' 32 | } 33 | 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "material-start", 3 | "private": true, 4 | "version": "0.0.3", 5 | "description": "A starter project for AngularJS Material", 6 | "repository": "https://github.com/mess110/material-start", 7 | "license": "MIT", 8 | "devDependencies": { 9 | "bower": "^1.3.1", 10 | "del": "^2.0.2", 11 | "gulp": "^3.9.0", 12 | "gulp-coffee": "^2.3.1", 13 | "gulp-concat": "^2.6.0", 14 | "gulp-order": "^1.1.1", 15 | "gulp-processhtml": "^1.1.0", 16 | "gulp-sass": "^2.0.4", 17 | "gulp-util": "^3.0.6", 18 | "http-server": "^0.6.1", 19 | "karma": "~0.10", 20 | "karma-junit-reporter": "^0.2.2", 21 | "protractor": "^2.0.0", 22 | "selenium-webdriver": "^2.45.1", 23 | "shelljs": "^0.2.6" 24 | }, 25 | "scripts": { 26 | "postinstall": "bower update", 27 | "prestart": "npm update", 28 | "test": "protractor ./e2e/protractor.conf.js" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/avatar-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/avatar-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/google_plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/hangouts.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/ic_fullscreen_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/ic_fullscreen_exit_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/ic_music_note_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/ic_note_add_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/ic_view_list_48px.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/phone.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/assets/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: "Roboto", sans-serif; 3 | font-size: 14px; 4 | height: 100%; 5 | margin: 0px; 6 | padding: 0px; 7 | overflow: hidden; } 8 | 9 | ::-webkit-scrollbar { 10 | display: none; } 11 | 12 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { 13 | display: none !important; } 14 | 15 | .boobs { 16 | background: url("//media.giphy.com/media/EFIwNN4wo9Tig/giphy.gif") no-repeat center center; } 17 | 18 | .menu { 19 | background-color: transparent; 20 | border: none; 21 | height: 38px; 22 | margin: 16px; 23 | width: 36px; } 24 | 25 | md-list .md-button { 26 | color: inherit; 27 | font-weight: 500; 28 | text-align: left; 29 | width: 100%; } 30 | 31 | .selected:hover { 32 | background-color: #FFCDD2 !important; } 33 | 34 | .selected { 35 | background-color: #FFEBEE; } 36 | 37 | md-sidenav md-list { 38 | padding: 0px 0px 8px 0px; } 39 | 40 | #content { 41 | overflow: hidden; } 42 | #content .md-button.contact { 43 | background-color: transparent; 44 | border: none; 45 | width: 48px; 46 | height: 48px; 47 | margin: 8px auto 16px 0; 48 | position: absolute; 49 | top: 10px; 50 | right: 40px; } 51 | #content md-icon.avatar { 52 | margin-top: 10px; } 53 | #content .md-button.contact > md-icon { 54 | fill: black; 55 | width: 36px; 56 | height: 36px; } 57 | 58 | md-button.menuBtn > md-icon { 59 | fill: white; 60 | width: 24px; 61 | height: 24px; } 62 | 63 | #content .md-button.contact:active > md-icon { 64 | background-color: #dadada; 65 | border-radius: 75%; 66 | padding: 4px; 67 | transition: all 100ms ease-out 30ms; } 68 | 69 | .content-wrapper { 70 | position: relative; } 71 | 72 | md-toolbar h1 { 73 | font-size: 1.25em; 74 | font-weight: 400; } 75 | 76 | h1.center-it { 77 | margin: auto; } 78 | 79 | .avatar { 80 | position: relative; 81 | width: 128px; 82 | height: 128px; 83 | border: 1px solid #ddd; 84 | border-radius: 50%; 85 | display: inline-block; 86 | overflow: hidden; 87 | margin: 0px; 88 | vertical-align: middle; 89 | zoom: 0.7; 90 | transform: translateZ(0); 91 | -webkit-transform: scale(0.7); 92 | -moz-transform: scale(0.7); } 93 | 94 | md-bottom-sheet md-icon { 95 | margin-right: 20px; } 96 | 97 | span.name { 98 | font-weight: bold; 99 | font-size: 1.1em; 100 | padding-left: 5px; } 101 | 102 | .small-bot { 103 | padding-top: 6px !important; 104 | padding-bottom: 2px !important; } 105 | 106 | .done { 107 | text-decoration: line-through; } 108 | 109 | form[name=userForm] md-input-container { 110 | padding-left: 12px; 111 | padding-right: 12px; } 112 | 113 | input { 114 | color: rgba(0, 0, 0, 0.87) !important; } 115 | 116 | /* you can also define the transition style 117 | * on the base class as well (.repeat-item) */ 118 | .repeat-item.ng-enter, .repeat-item.ng-leave { 119 | -webkit-transition: 0.5s linear all; 120 | transition: 0.5s linear all; } 121 | .repeat-item.ng-enter { 122 | opacity: 0; } 123 | .repeat-item.ng-leave { 124 | opacity: 1; } 125 | .repeat-item.ng-leave.ng-leave-active { 126 | opacity: 0; } 127 | .repeat-item.ng-enter.ng-enter-active { 128 | opacity: 1; } 129 | 130 | .delete { 131 | float: right; } 132 | 133 | label.add-new { 134 | white-space: nowrap; 135 | overflow: hidden; 136 | text-overflow: ellipsis; } 137 | 138 | /*# sourceMappingURL=app.css.map */ 139 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/css/app.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAAA,UAAU;EACR,WAAW,EAAE,oBAAoB;EACjC,SAAS,EAAE,IAAI;EACf,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,GAAG;EACZ,QAAQ,EAAE,MAAM;;AAElB,mBAAmB;EACjB,OAAO,EAAE,IAAI;;AAEf,8EAA8E;EAC5E,OAAO,EAAE,eAAe;;AAE1B,MAAM;EACJ,UAAU,EAAE,8EAA8E;;AAE5F,KAAK;EACH,gBAAgB,EAAE,WAAW;EAC7B,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,IAAI;EACZ,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,IAAI;;AAEb,kBAAkB;EAChB,KAAK,EAAE,OAAO;EACd,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,IAAI;EAChB,KAAK,EAAE,IAAI;;AAIb,eAAe;EACb,gBAAgB,EAAE,kBAAkB;;AAEtC,SAAS;EACP,gBAAgB,EAAE,OAAO;;AAE3B,kBAAkB;EAChB,OAAO,EAAE,eAAe;;AAE1B,QAAQ;EACN,QAAQ,EAAE,MAAM;EAChB,2BAAkB;IAChB,gBAAgB,EAAE,WAAW;IAC7B,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,IAAI;IACT,KAAK,EAAE,IAAI;EACb,uBAAc;IACZ,UAAU,EAAE,IAAI;EAClB,qCAA4B;IAC1B,IAAI,EAAE,KAAK;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;;AAEhB,2BAA2B;EACzB,IAAI,EAAE,KAAK;EACX,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAGZ,4CAAmC;EACjC,gBAAgB,EAAE,OAAO;EACzB,aAAa,EAAE,GAAG;EAClB,OAAO,EAAE,GAAG;EACZ,UAAU,EAAE,uBAAuB;;AAMvC,gBAAgB;EACd,QAAQ,EAAE,QAAQ;;AAEpB,aAAa;EACX,SAAS,EAAE,MAAO;EAClB,WAAW,EAAE,GAAG;;AAElB,YAAY;EACV,MAAM,EAAE,IAAI;;AAEd,OAAO;EACL,QAAQ,EAAE,QAAQ;EAClB,KAAK,EAAE,KAAK;EACZ,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,cAAc;EACtB,aAAa,EAAE,GAAG;EAClB,OAAO,EAAE,YAAY;EACrB,QAAQ,EAAE,MAAM;EAChB,MAAM,EAAE,GAAG;EACX,cAAc,EAAE,MAAM;EACtB,IAAI,EAAE,GAAI;EACV,SAAS,EAAE,aAAa;EACxB,iBAAiB,EAAE,UAAU;EAC7B,cAAc,EAAE,UAAU;;AAE5B,uBAAuB;EACrB,YAAY,EAAE,IAAI;;AAEpB,SAAS;EACP,WAAW,EAAE,IAAI;EACjB,SAAS,EAAE,KAAK;EAChB,YAAY,EAAE,GAAG;;AAEnB,UAAU;EACR,WAAW,EAAE,cAAc;EAC3B,cAAc,EAAE,cAAc;;AAEhC,KAAK;EACH,eAAe,EAAE,YAAY;;AAE/B,sCAAsC;EACpC,YAAY,EAAE,IAAI;EAClB,aAAa,EAAE,IAAI;;AAErB,KAAK;EACH,KAAK,EAAE,8BAA2B;;;;AAMlC,4CAAsB;EACpB,kBAAkB,EAAE,eAAe;EACnC,UAAU,EAAE,eAAe;AAC7B,qBAAU;EACR,OAAO,EAAE,CAAC;AACZ,qBAAU;EAGR,OAAO,EAAE,CAAC;EAFV,qCAAiB;IACf,OAAO,EAAE,CAAC;AAEd,qCAA0B;EACxB,OAAO,EAAE,CAAC;;AAEd,OAAO;EACL,KAAK,EAAE,KAAK;;AAEd,aAAa;EACX,WAAW,EAAE,MAAM;EACnB,QAAQ,EAAE,MAAM;EAChB,aAAa,EAAE,QAAQ", 4 | "sources": ["app.sass"], 5 | "names": [], 6 | "file": "app.css" 7 | } -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/css/app.sass: -------------------------------------------------------------------------------- 1 | html, body 2 | font-family: 'Roboto', sans-serif 3 | font-size: 14px 4 | height: 100% 5 | margin: 0px 6 | padding: 0px 7 | overflow: hidden 8 | 9 | ::-webkit-scrollbar 10 | display: none 11 | 12 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak 13 | display: none !important 14 | 15 | .boobs 16 | background: url("//media.giphy.com/media/EFIwNN4wo9Tig/giphy.gif") no-repeat center center 17 | 18 | .menu 19 | background-color: transparent 20 | border: none 21 | height: 38px 22 | margin: 16px 23 | width: 36px 24 | 25 | md-list .md-button 26 | color: inherit 27 | font-weight: 500 28 | text-align: left 29 | width: 100% 30 | // &.selected 31 | // color: #03a9f4 32 | 33 | .selected:hover 34 | background-color: #FFCDD2 !important 35 | 36 | .selected 37 | background-color: #FFEBEE 38 | 39 | md-sidenav md-list 40 | padding: 0px 0px 8px 0px 41 | 42 | #content 43 | overflow: hidden 44 | .md-button.contact 45 | background-color: transparent 46 | border: none 47 | width: 48px 48 | height: 48px 49 | margin: 8px auto 16px 0 50 | position: absolute 51 | top: 10px 52 | right: 40px 53 | md-icon.avatar 54 | margin-top: 10px 55 | .md-button.contact > md-icon 56 | fill: black 57 | width: 36px 58 | height: 36px 59 | 60 | md-button.menuBtn > md-icon 61 | fill: white 62 | width: 24px 63 | height: 24px 64 | 65 | #content 66 | .md-button.contact:active > md-icon 67 | background-color: #dadada 68 | border-radius: 75% 69 | padding: 4px 70 | transition: all 100ms ease-out 30ms 71 | // img 72 | // display: block 73 | // height: auto 74 | // max-width: 500px 75 | 76 | .content-wrapper 77 | position: relative 78 | 79 | md-toolbar h1 80 | font-size: 1.250em 81 | font-weight: 400 82 | 83 | h1.center-it 84 | margin: auto 85 | 86 | .avatar 87 | position: relative 88 | width: 128px 89 | height: 128px 90 | border: 1px solid #ddd 91 | border-radius: 50% 92 | display: inline-block 93 | overflow: hidden 94 | margin: 0px 95 | vertical-align: middle 96 | zoom: 0.70 97 | transform: translateZ(0) 98 | -webkit-transform: scale(0.7) 99 | -moz-transform: scale(0.7) 100 | 101 | md-bottom-sheet md-icon 102 | margin-right: 20px 103 | 104 | span.name 105 | font-weight: bold 106 | font-size: 1.1em 107 | padding-left: 5px 108 | 109 | .small-bot 110 | padding-top: 6px !important 111 | padding-bottom: 2px !important 112 | 113 | .done 114 | text-decoration: line-through 115 | 116 | form[name=userForm] md-input-container 117 | padding-left: 12px 118 | padding-right: 12px 119 | 120 | input 121 | color: rgba(0,0,0,0.87) !important 122 | 123 | /* you can also define the transition style 124 | * on the base class as well (.repeat-item) 125 | 126 | .repeat-item 127 | &.ng-enter, &.ng-leave 128 | -webkit-transition: 0.5s linear all 129 | transition: 0.5s linear all 130 | &.ng-enter 131 | opacity: 0 132 | &.ng-leave 133 | &.ng-leave-active 134 | opacity: 0 135 | opacity: 1 136 | &.ng-enter.ng-enter-active 137 | opacity: 1 138 | 139 | .delete 140 | float: right 141 | 142 | label.add-new 143 | white-space: nowrap 144 | overflow: hidden 145 | text-overflow: ellipsis 146 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/app.coffee: -------------------------------------------------------------------------------- 1 | app = angular.module('app', ['ngRoute', 'ngStorage', 'ngMaterial', 'users']) 2 | 3 | app.constant("myConfig", 4 | title: 'mango {fruit}' 5 | ) 6 | 7 | app.config ['$mdThemingProvider', '$mdIconProvider', ($mdThemingProvider, $mdIconProvider) -> 8 | $mdIconProvider 9 | .defaultIconSet('./assets/svg/avatars.svg', 128) 10 | .icon('menu', './assets/svg/menu.svg', 24) 11 | .icon('share', './assets/svg/share.svg', 24) 12 | .icon('google_plus', './assets/svg/google_plus.svg', 512) 13 | .icon('hangouts', './assets/svg/hangouts.svg', 512) 14 | .icon('twitter', './assets/svg/twitter.svg', 512) 15 | .icon('phone', './assets/svg/phone.svg', 512) 16 | .icon('person_add', './assets/svg/ic_person_add_48px.svg', 48) 17 | .icon('clear', './assets/svg/ic_clear_48px.svg', 48) 18 | .icon('plus', './assets/svg/ic_add_48px.svg', 48) 19 | .icon('cog', './assets/svg/ic_settings_48px.svg', 48) 20 | .icon('delete', './assets/svg/ic_delete_48px.svg', 48) 21 | .icon('lock', './assets/svg/ic_lock_48px.svg', 48) 22 | .icon('share', './assets/svg/ic_share_48px.svg', 48) 23 | .icon('music', './assets/svg/ic_music_note_48px.svg', 48) 24 | .icon('note_add', './assets/svg/ic_note_add_48px.svg', 48) 25 | .icon('fullscreen', './assets/svg/ic_fullscreen_48px.svg', 48) 26 | .icon('fullscreen_exit', './assets/svg/ic_fullscreen_exit_48px.svg', 48) 27 | 28 | $mdThemingProvider 29 | .theme('default') 30 | .primaryPalette('deep-orange') 31 | .accentPalette('blue') 32 | .warnPalette('red') 33 | .backgroundPalette('grey') 34 | # .dark() 35 | 36 | return 37 | ] 38 | 39 | app.config ['$routeProvider', ($routeProvider) -> 40 | $routeProvider 41 | .when('/tutorial', templateUrl: 'js/tutorial/tutorial.html') 42 | .when('/blobs', templateUrl: 'js/blobs/blobs.html', controller: 'BlobsController') 43 | .when('/blobs/:id', templateUrl: 'js/blobs/blobs.html', controller: 'BlobsController') 44 | .otherwise(redirectTo: '/blobs') 45 | return 46 | ] 47 | 48 | app.config(['$localStorageProvider', ($localStorageProvider) -> 49 | # $localStorageProvider.get('MyKey'); 50 | # $localStorageProvider.set('MyKey', { k: 'value' }); 51 | 52 | $localStorageProvider.setKeyPrefix 'app' 53 | return 54 | ]) 55 | 56 | app.run ($rootScope, myConfig) -> 57 | $rootScope.myConfig = myConfig 58 | return 59 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/blobs/BlobsController.coffee: -------------------------------------------------------------------------------- 1 | BlobsController = ($scope, $routeParams, $location, $localStorage, $mdBottomSheet) -> 2 | if !$routeParams.id? and $localStorage.selected? 3 | $location.path "/blobs/#{$localStorage.selected.api_key}" 4 | return 5 | 6 | $scope.fullItem = null 7 | 8 | if $routeParams.id? 9 | unless $localStorage.selected? 10 | $location.path '/blobs' 11 | return 12 | 13 | $scope.user = $localStorage.selected 14 | jNorthPole.getStorage($scope.user, (data) -> 15 | $scope.items = data.reverse() 16 | $scope.$apply() 17 | , (error) -> 18 | console.log error 19 | ) 20 | else 21 | $location.path '/tutorial' 22 | return 23 | 24 | $scope.delete = (item) -> 25 | item = angular.copy(item) 26 | item.secret = $scope.user.secret 27 | 28 | jNorthPole.deleteStorage(item, (data) -> 29 | for r in $scope.items 30 | if r.id == item.id 31 | $scope.items.splice $scope.items.indexOf(r), 1 32 | break 33 | $scope.$apply() 34 | , (error) -> 35 | console.log error 36 | ) 37 | return 38 | 39 | $scope.addItemToList = (item, s) -> 40 | return if s == '' 41 | item.list = [] unless item.list? 42 | item.list.push { checked: false, text: s } 43 | $scope.save(item) 44 | 45 | $scope.removeItemFromList = (item, listItem) -> 46 | index = item.list.indexOf(listItem) 47 | item.list.splice(index, 1) 48 | $scope.save(item) 49 | 50 | $scope.fullscreen = (item) -> 51 | if $scope.fullItem? 52 | $scope.fullItem = null 53 | else 54 | $scope.fullItem = item 55 | 56 | $scope.toggleType = (item) -> 57 | if item.type == 'list' 58 | item.type = 'text' 59 | else 60 | item.type = 'list' 61 | $scope.save(item) 62 | 63 | $scope.save = (item) -> 64 | item = angular.copy(item) 65 | item.api_key = $scope.user.api_key 66 | item.secret = $scope.user.secret 67 | 68 | callback = (data) -> 69 | console.log data 70 | 71 | if item.id? 72 | jNorthPole.putStorage(item, callback) 73 | else 74 | jNorthPole.createStorage(item, (data) -> 75 | $scope.items.unshift data 76 | $scope.$apply() 77 | , callback) 78 | 79 | $scope.newPanel = -> 80 | $scope.save(text: '') 81 | 82 | $scope.showContactOptions = ($event, json) -> 83 | json = angular.copy(json) 84 | 85 | ContactPanelController = ($mdBottomSheet, $scope) -> 86 | @json = json 87 | @actions = [ 88 | { 89 | name: 'Yes, delete already' 90 | icon: 'delete' 91 | } 92 | ] 93 | 94 | @submitContact = (action) -> 95 | $mdBottomSheet.hide action 96 | return 97 | 98 | return 99 | 100 | $mdBottomSheet 101 | .show( 102 | parent: angular.element(document.body) 103 | templateUrl: './js/templates/contactSheet.html' 104 | controller: ['$mdBottomSheet', '$scope', ContactPanelController] 105 | controllerAs: 'cp' 106 | bindToController: true 107 | targetEvent: $event) 108 | .then (clickedItem) -> 109 | if clickedItem.icon == 'delete' 110 | $scope.delete(json) 111 | return 112 | 113 | return 114 | 115 | angular.module('users').controller 'BlobsController', ['$scope', '$routeParams', '$location', '$localStorage', '$mdBottomSheet', BlobsController] 116 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/blobs/blobs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 | 37 | 38 |
39 |

<< Swipe left for fortune

40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/tutorial/tutorial.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Mango The Polar Bear helps you get your &!@^% together. 5 |

6 |

With client side encryption

7 |

Of impossible decryption,

8 |

Your ideas and your notes

9 |

Will floats your boats.

10 |
11 |

12 | Data is stored at the North Pole. 13 |

14 |
15 |
16 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/users/UserService.coffee: -------------------------------------------------------------------------------- 1 | UserService = ($q, $localStorage) -> 2 | unless $localStorage.accounts? 3 | $localStorage.accounts = [] 4 | 5 | users = $localStorage.accounts 6 | 7 | { 8 | loadAllUsers: -> 9 | $q.when users 10 | } 11 | 12 | angular.module('users').service 'userService', ['$q', '$localStorage', UserService] 13 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/users/Users.coffee: -------------------------------------------------------------------------------- 1 | angular.module 'users', [ 'ngMaterial' ] 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/app/src/js/users/users.html: -------------------------------------------------------------------------------- 1 | 2 |

{{ul.selected.name}}

3 |

{{ul.selected.content}}

4 | 5 | 6 | Contact {{ ul.selected.name }} 7 | 8 | 9 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/favicon.ico -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/font/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/font/FontAwesome.otf -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/font/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.eot -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/font/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/font/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.woff -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/bear.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/bg.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/cloud.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/dotnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/dotnet.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/dotnet_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/dotnet_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/footer.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/ice.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/java.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/java_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/java_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/js.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/js.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/json.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/json_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/json_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/logo.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/mobile.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/php.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/php_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/php_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/python.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/python_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/python_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/ruby.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/ruby_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/ruby_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/shell.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/images/shell_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/json.northpole.ro/public/images/shell_small.png -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/javascripts/analytics.js: -------------------------------------------------------------------------------- 1 | var _gaq = _gaq || []; 2 | _gaq.push(['_setAccount', 'UA-36922353-1']); 3 | _gaq.push(['_trackPageview']); 4 | 5 | (function() { 6 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; 7 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 8 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 9 | })(); 10 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/javascripts/app.coffee: -------------------------------------------------------------------------------- 1 | reqSuccess = (data) -> 2 | str = JSON.stringify(data, `undefined`, 2) 3 | output = $('#parsedJson') 4 | if (output.length > 0) 5 | output[0].innerHTML = syntaxHighlight(str) 6 | 7 | if typeof String::endsWith isnt "function" 8 | String::endsWith = (suffix) -> 9 | @indexOf(suffix, @length - suffix.length) isnt -1 10 | 11 | setActiveMenuClass = () -> 12 | checkFor = (s) -> 13 | if document.URL.endsWith(s) 14 | $('.' + s).addClass('active') 15 | 16 | checkFor 'playground' 17 | checkFor 'examples' 18 | checkFor 'faq' 19 | 20 | randomlyFlipBear = () -> 21 | if Math.random() > 0.5 22 | $(".bear").toggleClass('flipit') 23 | 24 | window.syntaxHighlight = (json) -> 25 | json = json.replace(/&/g, "&").replace(//g, ">") 26 | json.replace /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, (match) -> 27 | cls = "number value" 28 | if /^"/.test(match) 29 | if /:$/.test(match) 30 | cls = "key" 31 | else 32 | cls = "string value" 33 | else if /true|false/.test(match) 34 | cls = "boolean value" 35 | else cls = "null value" if /null/.test(match) 36 | "" + match + "" 37 | 38 | $(document).ready -> 39 | # jNorthPole.BASE_URL = 'http://localhost:1337/' 40 | setActiveMenuClass() 41 | setInterval(randomlyFlipBear, 1000) 42 | 43 | $('#playground').focus() 44 | 45 | $(".dropdown-menu li a").click () -> 46 | selText = $(this).text() 47 | $(this).parents('.btn-group').find('.dropdown-toggle span')[0].innerHTML = selText 48 | 49 | $('#myTab a').click (e) -> 50 | e.preventDefault() 51 | $(this).tab('show') 52 | window.location.hash = $(this)[0].hash 53 | 54 | if window.location.hash? 55 | $("#myTab a[href='#{window.location.hash}']").tab('show') 56 | 57 | $('#npAction').click () -> 58 | verb = $('#verb').text(); 59 | verb = 'SEARCH' if verb == 'GET' 60 | 61 | resource = $('#resource').text() 62 | try 63 | jsonObj = JSON.parse(($('#playground').val())) 64 | catch err 65 | reqSuccess(JSON.parse('{"error": "invalid json input"}')) 66 | return 67 | 68 | jNorthPole.genericRequest(jsonObj, verb, resource, reqSuccess) 69 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/javascripts/jnorthpole.coffee: -------------------------------------------------------------------------------- 1 | window.jNorthPole = 2 | 3 | BASE_URL: 'https://json.northpole.ro/' 4 | 5 | help: """ 6 | NorthPole JS wrapper example usage: 7 | 8 | responseHandler = function (data) { 9 | console.log(data); 10 | }; 11 | 12 | jNorthPole.getStorage(json, responseHandler); 13 | 14 | socket = jNorthPole.getNewRealtimeSocket(responseHandler) 15 | jNorthPole.subscribe(socket, 'foo') 16 | jNorthPole.publish(socket, 'foo', { message: 'hello' }) 17 | """ 18 | 19 | genericRequest: (jsonObj, method, endPoint, responseHandler, errorHandler=responseHandler) -> 20 | throw 'responseHandler function missing' unless responseHandler? 21 | 22 | r = new XMLHttpRequest 23 | r.open method, "#{@BASE_URL}#{endPoint}.json", true 24 | 25 | r.onreadystatechange = -> 26 | return if r.readyState != 4 27 | if r.status == 200 28 | responseHandler(JSON.parse(r.responseText), r.status) 29 | else 30 | errorHandler(JSON.parse(r.responseText), r.status) 31 | return 32 | r.send JSON.stringify(jsonObj) 33 | return 34 | 35 | createUser: (api_key, secret, success, failure) -> 36 | jsonObj = {'api_key': api_key, 'secret': secret} 37 | @genericRequest(jsonObj, 'POST', 'user', success, failure) 38 | return 39 | 40 | getUser: (jsonObj, responseHandler, errorHandler) -> 41 | @genericRequest(jsonObj, 'SEARCH', 'user', responseHandler, errorHandler) 42 | return 43 | 44 | createStorage: (jsonObj, responseHandler, errorHandler) -> 45 | @genericRequest(jsonObj, 'POST', 'storage', responseHandler, errorHandler) 46 | return 47 | 48 | getStorage: (jsonObj, responseHandler, errorHandler) -> 49 | @genericRequest(jsonObj, 'SEARCH', 'storage', responseHandler, errorHandler) 50 | return 51 | 52 | putStorage: (jsonObj, responseHandler, errorHandler) -> 53 | @genericRequest(jsonObj, 'PUT', 'storage', responseHandler, errorHandler) 54 | return 55 | 56 | deleteStorage: (jsonObj, responseHandler, errorHandler) -> 57 | @genericRequest(jsonObj, 'DELETE', 'storage', responseHandler, errorHandler) 58 | return 59 | 60 | getNewRealtimeSocket: (responseHandler, errorHandler=responseHandler) -> 61 | socketUrl = @BASE_URL.replace('http', 'ws') 62 | socket = new WebSocket("#{socketUrl}realtime") 63 | socket.onmessage = responseHandler 64 | socket.onclose = errorHandler 65 | socket 66 | 67 | subscribe: (socket, channel_name) -> 68 | socket.send(JSON.stringify( 69 | type: 'subscribe' 70 | channel_name: channel_name 71 | )) 72 | 73 | unsubscribe: (socket, channel_name) -> 74 | socket.send(JSON.stringify( 75 | type: 'unsubscribe' 76 | channel_name: channel_name 77 | )) 78 | 79 | publish: (socket, channel_name, json) -> 80 | socket.send(JSON.stringify( 81 | type: 'publish' 82 | channel_name: channel_name 83 | content: json 84 | )) 85 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/javascripts/realtime.coffee: -------------------------------------------------------------------------------- 1 | $(document).ready -> 2 | receivedMessages = [] 3 | 4 | output = $('.realtime-output') 5 | return if output.length <= 0 6 | 7 | socket = jNorthPole.getNewRealtimeSocket((data) -> 8 | console.log data 9 | return unless data.data? 10 | json = JSON.parse(data.data) 11 | return unless json.messages? 12 | 13 | for message in json.messages 14 | if $.inArray(message.id, receivedMessages) == -1 15 | receivedMessages.push(message.id) 16 | output.append("#{message.content.message}
") 17 | ) 18 | setTimeout -> 19 | jNorthPole.subscribe(socket, 'jNorthPoleChat') 20 | , 2000 21 | 22 | $('.realtime-input').keypress((event) -> 23 | keycode = if event.keyCode then event.keyCode else event.which 24 | if (keycode == 13) 25 | inptz = $(@) 26 | jNorthPole.publish(socket, 'jNorthPoleChat', { message: inptz.val() }) 27 | inptz.val('') 28 | ) 29 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/music/music.coffee: -------------------------------------------------------------------------------- 1 | $(() -> 2 | $.material.init() 3 | ) 4 | 5 | app = angular.module('app', [ 'youtube-embed' ]) 6 | 7 | app.controller 'MainCtrl', ($scope, youtubeEmbedUtils) -> 8 | 9 | $scope.refresh = -> 10 | if $scope.loggedIn 11 | $scope.loggedIn = false 12 | $scope.results = [] 13 | localStorage.clear() 14 | $scope.json = 15 | 'api_key': 'guest' 16 | 'secret': 'guest' 17 | 'category': 'music' 18 | else 19 | $scope.loggedIn = false 20 | jNorthPole.getStorage $scope.json, (data) -> 21 | if $scope.json.apiKey != 'guest' and !data.error? 22 | localStorage.json = angular.toJson($scope.json) 23 | unless data.error? and data.api_key != 'guest' 24 | $scope.apiKey = $scope.json.apiKey 25 | $scope.results = data 26 | $scope.loggedIn = true 27 | $scope.$apply() 28 | return 29 | 30 | return 31 | 32 | jNorthPole.BASE_URL = 'https://json.northpole.ro/' 33 | $scope.results = [] 34 | $scope.playerVars = 35 | controls: 1 36 | autoplay: 1 37 | if localStorage.json == undefined 38 | $scope.json = 39 | 'api_key': 'guest' 40 | 'secret': 'guest' 41 | 'category': 'music' 42 | else 43 | $scope.json = angular.fromJson(localStorage.json) 44 | $scope.refresh() 45 | 46 | $scope.play = (video) -> 47 | return unless video? 48 | if $scope.ytVideoUrl == video.url and $scope.bestPlayer? and $scope.bestPlayer.getPlayerState() == 1 49 | $scope.bestPlayer.pauseVideo() 50 | else 51 | $scope.ytVideoUrl = video.url 52 | $scope.bestPlayer.playVideo() if $scope.bestPlayer? 53 | # console.log youtubeEmbedUtils.getIdFromURL($scope.ytVideoUrl) 54 | return 55 | 56 | $scope.edit = (video) -> 57 | $scope.selected = video 58 | return 59 | 60 | $scope.save = (video) -> 61 | $scope.selected.secret = $scope.json.secret 62 | json = angular.copy($scope.selected) 63 | 64 | log = (data) -> 65 | console.log data 66 | return 67 | 68 | if video.id? 69 | jNorthPole.putStorage json, log 70 | else 71 | json.api_key = $scope.json.api_key 72 | json.category = 'music' 73 | jNorthPole.createStorage json, log 74 | $('#complete-dialog').modal('hide') 75 | $scope.selected = undefined 76 | return 77 | 78 | $scope.newSong = -> 79 | $scope.selected = {} 80 | return 81 | 82 | $scope.select = (json) -> 83 | $scope.selected = json 84 | return 85 | 86 | $scope.isSelected = (video) -> 87 | $scope.ytVideoUrl == video.url 88 | 89 | $scope.playRandom = -> 90 | item = $scope.results[Math.floor(Math.random() * $scope.results.length)] 91 | $scope.play item 92 | if player? 93 | player.seekTo 0 94 | return 95 | 96 | $scope.$on 'youtube.player.ended', ($event, player) -> 97 | $scope.playRandom player 98 | return 99 | return 100 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/music/music.sass: -------------------------------------------------------------------------------- 1 | $size: 56px 2 | 3 | html, body 4 | height: 100% 5 | 6 | .bear 7 | background: url(/images/bear.png) no-repeat center center 8 | 9 | .embed-responsive 10 | position: relative 11 | display: block 12 | height: 0 13 | padding: 0 14 | overflow: hidden 15 | 16 | .embed-responsive.embed-responsive-16by9 17 | padding-bottom: 56.25% 18 | 19 | .embed-responsive-item 20 | @extend .bear 21 | position: absolute 22 | top: 0 23 | bottom: 0 24 | left: 0 25 | width: 100% 26 | height: 100% 27 | border: 0 28 | 29 | .container 30 | z-index: 10 31 | 32 | .content-container 33 | padding-top: $size + 10px 34 | padding-bottom: $size - 10px 35 | 36 | .add-button 37 | margin-bottom: 10px !important 38 | margin-right: 10px !important 39 | position: fixed 40 | right: 0 41 | bottom: 0 42 | 43 | .no-wrap 44 | white-space: nowrap 45 | overflow: hidden 46 | text-overflow: ellipsis 47 | 48 | .vertical-center 49 | display: flex 50 | align-items: center 51 | 52 | p.disabled 53 | color: #bdbdbd 54 | 55 | .selected 56 | background-color: red !important 57 | p 58 | color: #f5f5f5 59 | h4 60 | color: #f5f5f5 !important 61 | 62 | .list-group-separator 63 | background-color: white 64 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/public/stylesheets/app.sass: -------------------------------------------------------------------------------- 1 | body 2 | padding-top: 20px 3 | padding-bottom: 20px 4 | 5 | $text-color: #333 6 | 7 | .navbar 8 | margin-bottom: 20px 9 | 10 | .tab-content 11 | padding-top: 10px 12 | 13 | .flipit 14 | -moz-transform: scaleX(-1) 15 | -o-transform: scaleX(-1) 16 | -webkit-transform: scaleX(-1) 17 | transform: scaleX(-1) 18 | filter: FlipH 19 | -ms-filter: "FlipH" 20 | 21 | .bear 22 | margin: auto 23 | 24 | .code 25 | text-align: justify 26 | 27 | #parsedJson 28 | text-align: left 29 | 30 | .value 31 | // color: darken($cool, 20%) 32 | font-weight: bold 33 | 34 | #playground 35 | width: 100% 36 | height: 300px 37 | color: $text-color 38 | padding: 15px 39 | font-family: Menlo,Monaco,Consolas,'Courier New',monospace 40 | background-color: #f5f5f5 41 | 42 | input[type="text"], textarea 43 | outline: none 44 | box-shadow: none !important 45 | 46 | .form-control:focus 47 | border-color: #ccc 48 | 49 | .realtime-input 50 | background-color: #f5f5f5 51 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/curl.haml: -------------------------------------------------------------------------------- 1 | %pre.code 2 | = preserve do 3 | :escaped 4 | curl -X POST -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","storage":["milk","eggs","bacon"]}' '#{NP_URL}/storage.json' 5 | 6 | %pre.code 7 | = preserve do 8 | :escaped 9 | curl -X GET -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","storage":["milk","eggs","bacon"]}' '#{NP_URL}/storage.json' 10 | 11 | %pre.code 12 | = preserve do 13 | :escaped 14 | curl -X PUT -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","storage":["milk","eggs"],"id": "OBJECT_ID"}' '#{NP_URL}/storage.json' 15 | 16 | %pre.code 17 | = preserve do 18 | :escaped 19 | curl -X DELETE -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","storage":["milk","eggs"]}' '#{NP_URL}/storage.json' 20 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/doc.haml: -------------------------------------------------------------------------------- 1 | %p Create 2 | %pre.code 3 | = preserve do 4 | curl -X POST -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","namespace":"shopping_list","storage":["milk","eggs","bacon"]}' '#{NP_URL}/storage.json' 5 | %br 6 | %p Read 7 | %p.code{:style => 'text-align: left; background-color: #f5f5f5; color: #333; padding: 9.5px; font-family: Menlo,Monaco,Consolas,"Courier New",monospace;'} 8 | curl -X GET -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_API_KEY}","id":"534569007047d56e11000002"}' '#{NP_URL}/storage.json' 9 | %br 10 | %p Update 11 | %pre.code.shell 12 | curl -X PUT -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","food":[],"id":"534569007047d56e11000002"}' '#{NP_URL}/storage.json' 13 | %br 14 | %p Delete 15 | %pre.code.shell 16 | curl -X DELETE -d '{"api_key":"#{NP_GUEST_API_KEY}","secret":"#{NP_GUEST_SECRET}","id":"534569007047d56e11000002"}' '#{NP_URL}/storage.json' 17 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/doc_table.haml: -------------------------------------------------------------------------------- 1 | %table.table.table-striped 2 | %thead 3 | %tr 4 | %th Resource 5 | %th HTTP Verb 6 | %th Content-Type 7 | %th Required Params 8 | %th{'style' => 'width: 50%'} Description 9 | %tbody 10 | %tr 11 | %td /user 12 | %td GET, SEARCH 13 | %td application/json 14 | %td api_key, secret 15 | %td Finds a user and any info related to the user json object 16 | %tr 17 | %td /user 18 | %td POST 19 | %td application/json 20 | %td api_key, secret 21 | %td Creates a new user 22 | %tr 23 | %td /storage 24 | %td GET, SEARCH 25 | %td application/json 26 | %td api_key, secret 27 | %td Finds stored json objects. Results are always returned in an array 28 | %tr 29 | %td /storage 30 | %td POST 31 | %td application/json 32 | %td api_key, secret 33 | %td Creates a new json object 34 | %tr 35 | %td /storage 36 | %td PUT 37 | %td application/json 38 | %td api_key, secret, id 39 | %td Updates a json object. All attributes of the json object are replaced with the json found in the body of the request 40 | %tr 41 | %td /storage 42 | %td DELETE 43 | %td application/json 44 | %td api_key, secret 45 | %td Deletes json objects 46 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/dotnet.haml: -------------------------------------------------------------------------------- 1 | %p 2 | Thank you Rares for contributing with the 3 | %a{:href => 'https://github.com/northpole-api/NorthPoleNetWrapper/'} .net wrapper 4 | \. 5 | %p 6 | If you would like to dive in code imediatley, I suggest you take a look at the 7 | %a{:href => 'https://github.com/northpole-api/NorthPoleNetWrapper/blob/master/NorthPoleNetWrapperTest/StorageTestSuite.cs'} tests 8 | \. 9 | %pre.code.dotnet 10 | = preserve do 11 | :escaped 12 | JObject jsonObject = new JObject(); 13 | jsonObject.Add("#{NP_API_KEY}", "#{NP_GUEST_API_KEY}"); 14 | jsonObject.Add("#{NP_SECRET}", "#{NP_GUEST_SECRET}"); 15 | 16 | JArray items = new JArray(); 17 | items.Add("milk"); 18 | items.Add("eggs"); 19 | items.Add("sugar"); 20 | 21 | jsonObject.Add("list", items); 22 | 23 | Storage storage = new Storage(jsonObject); 24 | JObject result = storage.Create(); 25 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/examples.haml: -------------------------------------------------------------------------------- 1 | = partial "doc_table" 2 | 3 | %ul#myTab.nav.nav-tabs 4 | %li.active 5 | %a{"data-toggle" => "tab", href: "#curl"} 6 | %img{"src" => "/images/shell.png"} 7 | curl 8 | %li 9 | %a{"data-toggle" => "tab", href: "#javascript"} 10 | %img{"src" => "/images/js.png"} 11 | javascript 12 | %li 13 | %a{"data-toggle" => "tab", href: "#ruby"} 14 | %img{"src" => "/images/ruby.png"} 15 | ruby 16 | %li 17 | %a{"data-toggle" => "tab", href: "#python"} 18 | %img{"src" => "/images/python.png"} 19 | python 20 | %li 21 | %a{"data-toggle" => "tab", href: "#php"} 22 | %img{"src" => "/images/php.png"} 23 | php 24 | %li 25 | %a{"data-toggle" => "tab", href: "#java"} 26 | %img{"src" => "/images/java.png"} 27 | java 28 | %li 29 | %a{"data-toggle" => "tab", href: "#dotnet"} 30 | %img{"src" => "/images/dotnet.png"} 31 | \.net 32 | .tab-content 33 | #curl.tab-pane.fade.in.active 34 | = partial 'curl' 35 | #javascript.tab-pane.fade 36 | = partial 'javascript' 37 | #ruby.tab-pane.fade 38 | = partial 'ruby' 39 | #python.tab-pane.fade 40 | = partial 'python' 41 | #php.tab-pane.fade 42 | = partial 'php' 43 | #java.tab-pane.fade 44 | = partial 'java' 45 | #dotnet.tab-pane.fade 46 | = partial 'dotnet' 47 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/faq.haml: -------------------------------------------------------------------------------- 1 | %a{href: '#what'} What is json.northpole.ro? 2 | %br 3 | %a{href: '#how'} How do I use the API? 4 | %br 5 | %a{href: '#storage'} What can I store? 6 | %br 7 | %a{href: '#why'} Why should I register? 8 | %br 9 | %a{href: '#register'} How do I register? 10 | %br 11 | %a{href: '#realtime'} Is there an experimental realtime module? 12 | %br 13 | %h3#what What is json.northpole.ro? 14 | %p 15 | json.northpole.ro is a JSON cloud storage service. 16 | %p 17 | It is designed to be simple, fast and easy to use. Use a RESTful HTTP api to manage 18 | your json objects and access them from anywhere. 19 | %h3#how How do I use the API? 20 | %p 21 | HTTP requests. Read 22 | %a{href: '/examples'} the docs 23 | for more info. 24 | %p 25 | To get an idea of how the API works, head to the 26 | %a{href: '/playground'} playground 27 | \. 28 | %h3#storage What can I store? 29 | %p 30 | Any valid json object. The object can contain keys, arrays and hashes. 31 | %p 32 | Once the object gets created it will receive a unique id. 33 | %h3#why Why should I register? 34 | %p You get your own private storage space under your api_key and secret. 35 | %h3#register How do I register? 36 | %p 37 | Create a user in the 38 | %a{href: '/playground'} playground 39 | \. If you can't do that, there is a good chance you don't understand 40 | how the API works. Think of it as a tutorial. 41 | %h3#realtime Is there an experimental realtime module? 42 | %p 43 | Yes. Go to 44 | %a{href: '/websocket'} /websocket 45 | to give it a spin. 46 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/index.haml: -------------------------------------------------------------------------------- 1 | .jumbotron 2 | %a{:href => '/'} 3 | %img.bear.img-responsive{:src => '/images/bear.png', :alt => 'bear'} 4 | %h3.text-center manage JSON objects through HTTP requests 5 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/java.haml: -------------------------------------------------------------------------------- 1 | TODO 2 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/javascript.haml: -------------------------------------------------------------------------------- 1 | %p Open the browser console. 2 | %p This should get you started: 3 | %pre.code 4 | jNorthPole.help 5 | %p 6 | The js library can be found 7 | %a{href: '/javascripts/jnorthpole.js', target: '_blank'} here 8 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/layout.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html{lang: "en"} 3 | %head 4 | %meta{charset: "utf-8"}/ 5 | %meta{content: "IE=edge", "http-equiv" => "X-UA-Compatible"}/ 6 | %meta{content: "width=device-width, initial-scale=1", name: "viewport"}/ 7 | %meta{content: "", name: "description"}/ 8 | %meta{content: "", name: "author"}/ 9 | 10 | %link{href: "/favicon.ico", rel: "shortcut icon"}/ 11 | 12 | %title json cloud storage 13 | 14 | %link{href: "//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css", rel: "stylesheet"}/ 15 | = css "/stylesheets/app.css" 16 | / HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries 17 | /[if lt IE 9] 18 | 19 | 20 | %body 21 | .container 22 | .navbar.navbar-default{role: "navigation"} 23 | .container-fluid 24 | .navbar-header 25 | %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} 26 | %span.sr-only Toggle navigation 27 | %span.icon-bar 28 | %span.icon-bar 29 | %span.icon-bar 30 | %a.navbar-brand{href: "/"} {"json": ["cloud", "storage"]} 31 | .navbar-collapse.collapse 32 | %ul.nav.navbar-nav.navbar-right 33 | %li.playground 34 | %a{href: "/playground"} playground 35 | %li.examples 36 | %a{href: "/examples"} examples 37 | %li.faq 38 | %a{href: "/faq"} faq 39 | = yield 40 | 41 | = js "https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" 42 | = js "//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js" 43 | = js "/javascripts/jnorthpole.js" 44 | = js "/javascripts/realtime.js" 45 | = js "/javascripts/app.js" 46 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/music.haml: -------------------------------------------------------------------------------- 1 | :javascript 2 | window.location.href = "/music/index.html"; 3 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/php.haml: -------------------------------------------------------------------------------- 1 | %pre.code.php 2 | = preserve do 3 | :escaped 4 | {'id'}; 18 | 19 | $curlGet = curl_init(); 20 | curl_setopt($curlGet, CURLOPT_URL, $url . '?#{NP_API_KEY}=' . $apiKey . '&#{NP_SECRET}='. $secret .'&id=' . $resourceId); 21 | curl_setopt($curlGet, CURLOPT_RETURNTRANSFER, 1); 22 | $response = curl_exec($curlGet); 23 | 24 | $curlPut = curl_init(); 25 | curl_setopt($curlPut, CURLOPT_URL, $url . '?#{NP_API_KEY}=' . $apiKey . '&#{NP_SECRET}='. $secret .'&id=' . $resourceId . '&food=salad'); 26 | curl_setopt($curlPut, CURLOPT_RETURNTRANSFER, 1); 27 | curl_setopt($curlPut, CURLOPT_CUSTOMREQUEST, "PUT"); 28 | $response = curl_exec($curlPut); 29 | var_dump($response); 30 | 31 | $curlDelete = curl_init(); 32 | curl_setopt($curlDelete, CURLOPT_URL, $url . '?#{NP_API_KEY}=' . $apiKey . '&#{NP_SECRET}=' . $secret . '&id=' . $resourceId); 33 | curl_setopt($curlDelete, CURLOPT_RETURNTRANSFER, 1); 34 | curl_setopt($curlDelete, CURLOPT_CUSTOMREQUEST, "DELETE"); 35 | $response = curl_exec($curlDelete); 36 | 37 | ?> 38 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/playground.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .col-md-6 3 | .form-group 4 | %textarea#playground.form-control 5 | = preserve do 6 | { 7 | \ "api_key":"guest", 8 | \ "secret":"guest" 9 | } 10 | .text-center{style: 'margin-bottom: 14px;'} 11 | .btn-group 12 | %button.btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown", type: "button"} 13 | %span#resource Storage 14 | %span.caret 15 | %ul.dropdown-menu{role: "menu"} 16 | %li 17 | %a{href: "#"} Storage 18 | %li 19 | %a{href: "#"} User 20 | .btn-group 21 | %button.btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown", type: "button"} 22 | %span#verb GET 23 | %span.caret 24 | %ul.dropdown-menu{role: "menu"} 25 | %li 26 | %a{href: "#"} GET 27 | %li 28 | %a{href: "#"} POST 29 | %li 30 | %a{href: "#"} PUT 31 | %li 32 | %a{href: "#"} DELETE 33 | .btn-group 34 | %button#npAction.btn.btn-default Execute Request 35 | .col-md-6{:style => 'height: 100%'} 36 | %pre#parsedJson 37 | {} 38 | .row 39 | .col-md-12 40 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/python.haml: -------------------------------------------------------------------------------- 1 | %pre.code.python 2 | = preserve do 3 | :escaped 4 | import requests 5 | import json 6 | 7 | API_KEY = '#{NP_GUEST_API_KEY}' 8 | SECRET = '#{NP_GUEST_SECRET}' 9 | URL = '#{NP_URL}/storage.json' 10 | 11 | result = requests.post(URL, params = {'#{NP_API_KEY}': API_KEY, '#{NP_SECRET}': SECRET, 'foo': 'bar'}) 12 | resource = json.loads(result.text) 13 | resource_id = resource['id'] 14 | 15 | requests.get(URL, params = {'#{NP_API_KEY}': API_KEY, '#{NP_SECRET}': SECRET, 'id': resource_id}) 16 | requests.put(URL, params = {'#{NP_API_KEY}': API_KEY, '#{NP_SECRET}': SECRET, 'id': resource_id, 'foo': 'not bar'}) 17 | requests.delete(URL, params = {'#{NP_API_KEY}': API_KEY, '#{NP_SECRET}': SECRET, 'id': resource_id}) 18 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/ruby.haml: -------------------------------------------------------------------------------- 1 | %pre.code 2 | = preserve do 3 | :escaped 4 | require 'uri' 5 | require 'net/http' 6 | require 'json' 7 | 8 | uri = URI.parse("#{NP_URL}") 9 | http = Net::HTTP.new(uri.host, uri.port) 10 | request = Net::HTTP::Post.new("/storage.json") 11 | request.body = {'#{NP_API_KEY}' => '#{NP_SECRET}', '#{NP_GUEST_API_KEY}' => '#{NP_GUEST_SECRET}'}.to_json 12 | response = http.request(request) 13 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/seed.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Hello 5 | %body 6 | = User.find_or_create({ NP_API_KEY => NP_GUEST_API_KEY, NP_SECRET => NP_GUEST_SECRET }) 7 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/thanks.haml: -------------------------------------------------------------------------------- 1 | %p 2 | = other_developer_count 3 | %br 4 | %p 5 | Thanks Gabi Nagy for 6 | %a{:href => 'https://github.com/northpole-api/northpole.ro/blob/master/public/favicon.ico'} the favicon 7 | %p 8 | Thanks Rares Musina for 9 | %a{:href => 'https://github.com/northpole-api/NorthPoleNetWrapper'} NorthPoleNetWrapper 10 | %p 11 | Thanks for 12 | %a{:href => 'http://openclipart.org/detail/97081/polar-bear-by-aguasas'} the bear 13 | -------------------------------------------------------------------------------- /spec/examples/json.northpole.ro/views/websocket.haml: -------------------------------------------------------------------------------- 1 | %p 2 | You are subscribed to the 3 | %b jNorthPoleChat 4 | channel. Everybody in this channel will be able to view your messages. 5 | %input.realtime-input.form-control{ type: 'text', placeholder: 'write your message and press enter' } 6 | %br 7 | %pre.realtime-output 8 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/.ruby-gemset: -------------------------------------------------------------------------------- 1 | tasks-example 2 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.0 2 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'ki' 4 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/README.md: -------------------------------------------------------------------------------- 1 | Ki Tasks Example 2 | ================ 3 | 4 | The framework has a built in task system. 5 | 6 | Tasks can be defined in the *tasks* directory and need to be valid ruby code. 7 | 8 | You can see a list of tasks by running: 9 | 10 | ``` 11 | ki tasks 12 | ``` 13 | 14 | To run a specific task: 15 | 16 | ``` 17 | ki tasks task_name 18 | ``` 19 | 20 | In this project, we have a task called hello_world. You can run it with: 21 | 22 | ``` 23 | ki tasks hello_world 24 | ``` 25 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/app.rb: -------------------------------------------------------------------------------- 1 | require 'ki' 2 | 3 | # class Message < Ki::Model 4 | # requires :title 5 | # forbid :delete 6 | # end 7 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | use Rack::Reloader, 0 3 | run Ki::Ki.new 4 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/config.yml: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: tasks-example_development 4 | host: 127.0.0.1 5 | port: 27017 6 | 7 | test: 8 | database: 9 | name: tasks-example_test 10 | host: 127.0.0.1 11 | port: 27017 12 | 13 | production: 14 | database: 15 | name: tasks-example 16 | host: 127.0.0.1 17 | port: 27017 18 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/tasks-example/public/favicon.ico -------------------------------------------------------------------------------- /spec/examples/tasks-example/public/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/tasks-example/public/javascripts/.gitkeep -------------------------------------------------------------------------------- /spec/examples/tasks-example/public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/tasks-example/public/stylesheets/.gitkeep -------------------------------------------------------------------------------- /spec/examples/tasks-example/tasks/hello_world.rb: -------------------------------------------------------------------------------- 1 | puts 'hello world' 2 | -------------------------------------------------------------------------------- /spec/examples/tasks-example/views/index.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Ki Framework 5 | %body 6 | %p Hello World 7 | -------------------------------------------------------------------------------- /spec/examples/todo/.ruby-gemset: -------------------------------------------------------------------------------- 1 | todo 2 | -------------------------------------------------------------------------------- /spec/examples/todo/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.2 2 | -------------------------------------------------------------------------------- /spec/examples/todo/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'ki' 4 | -------------------------------------------------------------------------------- /spec/examples/todo/app.rb: -------------------------------------------------------------------------------- 1 | require 'ki' 2 | 3 | module Ki::Helpers 4 | def say_hello 5 | 'hello' 6 | end 7 | end 8 | 9 | class Todo < Ki::Model 10 | requires :title 11 | end 12 | -------------------------------------------------------------------------------- /spec/examples/todo/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | use Rack::Reloader, 0 3 | run Ki::Ki.new 4 | -------------------------------------------------------------------------------- /spec/examples/todo/config.yml: -------------------------------------------------------------------------------- 1 | development: 2 | database: 3 | name: todo_development 4 | host: 127.0.0.1 5 | port: 27017 6 | 7 | test: 8 | database: 9 | name: todo_test 10 | host: 127.0.0.1 11 | port: 27017 12 | 13 | production: 14 | database: 15 | name: todo 16 | host: 127.0.0.1 17 | port: 27017 18 | -------------------------------------------------------------------------------- /spec/examples/todo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/todo/public/favicon.ico -------------------------------------------------------------------------------- /spec/examples/todo/public/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/todo/public/javascripts/.gitkeep -------------------------------------------------------------------------------- /spec/examples/todo/public/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mess110/ki/06f9f777267708dd7d57648b10778f17e407fed9/spec/examples/todo/public/stylesheets/.gitkeep -------------------------------------------------------------------------------- /spec/examples/todo/views/index.haml: -------------------------------------------------------------------------------- 1 | %p= say_hello 2 | -------------------------------------------------------------------------------- /spec/examples/todo/views/layout.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title Ki Framework 5 | %body 6 | = yield 7 | -------------------------------------------------------------------------------- /spec/functional_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'the app' do 6 | end 7 | -------------------------------------------------------------------------------- /spec/lib/ki/channel_manager_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::ChannelManager do 6 | let(:cm) { Ki::ChannelManager } 7 | let(:db) { Ki::Orm::Db.instance } 8 | 9 | it 'returns the db connection' do 10 | expect(cm.db).to eq db 11 | end 12 | 13 | it 'stores a socket on connection' do 14 | expect { 15 | cm.connect 16 | }.to change { db.count 'realtime_channel_sockets' }.by(1) 17 | end 18 | 19 | it 'removes a channel socket on disconnect' do 20 | socket = cm.connect 21 | expect { 22 | cm.disconnect(socket) 23 | }.to change { db.count 'realtime_channel_sockets' }.by(-1) 24 | end 25 | 26 | it 'removes all the channel subscriptions on disconnect' do 27 | socket = cm.connect 28 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel') 29 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel-2') 30 | expect { 31 | cm.disconnect(socket) 32 | }.to change { db.count 'realtime_channel_subscriptions' }.by(-2) 33 | end 34 | 35 | it 'returns the list of channel sockets' do 36 | cm.cleanup 37 | expect(cm.sockets).to be_empty 38 | 39 | cm.connect 40 | expect(cm.sockets).to_not be_empty 41 | end 42 | 43 | it 'subscribes to a channel' do 44 | socket = cm.connect 45 | 46 | expect { 47 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel') 48 | }.to change { db.count 'realtime_channel_subscriptions' }.by(1) 49 | end 50 | 51 | it 'does not subscribe to the same channel twice' do 52 | socket = cm.connect 53 | 54 | expect { 55 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel') 56 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel') 57 | }.to change { db.count 'realtime_channel_subscriptions' }.by(1) 58 | end 59 | 60 | it 'unsubscribes fom a channel' do 61 | socket = cm.connect 62 | cm.subscribe(socket_id: socket['id'], type: 'subscribe', 'channel_name' => 'test-channel') 63 | 64 | expect { 65 | cm.unsubscribe(socket_id: socket['id'], 'type' => 'unsubscribe', 'channel_name' => 'test-channel') 66 | }.to change { db.count 'realtime_channel_subscriptions' }.by(-1) 67 | end 68 | 69 | it 'returns messages from a tick' 70 | 71 | it 'publishes a message' do 72 | expect { 73 | item = cm.publish(hello: 'world') 74 | expect(item['created_at']).to_not be_nil # TODO: might need present? 75 | }.to change { db.count 'realtime_channel_messages' }.by(1) 76 | end 77 | 78 | it 'cleans up the data' do 79 | cm.cleanup 80 | expect(db.count('realtime_channel_sockets')).to eq 0 81 | expect(db.count('realtime_channel_subscriptions')).to eq 0 82 | expect(db.count('realtime_channel_messages')).to eq 0 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/lib/ki/helpers_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Helpers do 6 | include Ki::Helpers 7 | 8 | it 'should render_haml' do 9 | expect(haml('%div.foo')).to eq "
\n" 10 | end 11 | 12 | it 'renders css tag' do 13 | expect(css('asd')).to eq "\n" 14 | end 15 | 16 | it 'renders js tag' do 17 | expect(js('asd')).to eq "\n" 18 | end 19 | 20 | it 'renders 404 if partial not found' do 21 | expect { 22 | partial('does_not_exist') 23 | }.to raise_error Ki::PartialNotFoundError 24 | end 25 | 26 | it 'renders haml' do 27 | expect(File).to receive(:join).and_return('lib/ki/views/404.haml') 28 | expect(partial('404')).to include('h1') 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/lib/ki/indifferent_hash_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe IndifferentHash do 6 | let(:hash) { IndifferentHash.new } 7 | 8 | it 'works' do 9 | hash['asd'] = 1 10 | expect(hash[:asd]).to eq hash['asd'] 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/lib/ki/ki_app_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::KiApp do 6 | it 'html returns 404 if not found' do 7 | get '/invalid_url' 8 | expect(last_response.status).to eq 404 9 | end 10 | 11 | it 'json returns 404 if not found' do 12 | get '/invalid_url.json' 13 | expect(last_response.status).to eq 404 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/lib/ki/ki_config_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::KiConfig do 6 | let(:config) { Ki::KiConfig.instance } 7 | 8 | it 'should know db name in test env' do 9 | expect(config.environment).to eq 'test' 10 | end 11 | 12 | it 'is overwritten for testing. see spec_helper' do 13 | path = config.config_file_path 14 | expect(path).to be_start_with('spec/config.yml') 15 | end 16 | 17 | it 'defaults cors to true' do 18 | expect(config).to be_cors 19 | end 20 | 21 | it 'has a database method' do 22 | expect(config.database['name']).to eq 'np_test' 23 | end 24 | 25 | it 'has a middleware method' do 26 | expect(config.middleware).to_not be_empty 27 | end 28 | 29 | it 'rm_middleware' do 30 | config.config['rm_middleware'] = 'AdminInterfaceGenerator' 31 | expect(config.middleware).to_not include(Ki::Middleware::AdminInterfaceGenerator) 32 | end 33 | 34 | it 'add_middleware' do 35 | config.config['rm_middleware'] = 'Realtime' 36 | config.middleware 37 | expect(config.middleware).to_not include(Ki::Middleware::Realtime) 38 | config.config['rm_middleware'] = [] 39 | config.config['add_middleware'] = 'Realtime' 40 | expect(config.middleware).to include(Ki::Middleware::Realtime) 41 | end 42 | 43 | it 'add_middleware/rm_middleware accepts an array' do 44 | config.config['rm_middleware'] = 'Realtime' 45 | expect(config.middleware).to_not include(Ki::Middleware::Realtime) 46 | config.config['rm_middleware'] = [] 47 | 48 | config.config['add_middleware'] = ['Realtime'] 49 | expect(config.middleware).to include(Ki::Middleware::Realtime) 50 | end 51 | 52 | it 'does not add duplicate middleware' do 53 | config.config['add_middleware'] = %w[Realtime Realtime] 54 | expect(config.middleware.to_s.scan(/Realtime/).count).to eq 1 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /spec/lib/ki/ki_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Ki do 6 | class CorsResource < Ki::Model 7 | end 8 | 9 | it 'has CORS enabled' do 10 | get '/cors_resource.json' 11 | expect(last_response.headers.key?('Vary')).to be true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware/admin_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware::AdminInterfaceGenerator do 6 | it 'works' do 7 | get '/instadmin' 8 | expect(last_response.body).to include('Ki InstAdmin') 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware/haml_compiler_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware::HamlCompiler do 6 | let(:compiler) { Ki::Middleware::HamlCompiler } 7 | 8 | it 'works' do 9 | # TODO: find out why twice 10 | expect_any_instance_of(compiler).to receive(:view_exists?).twice.and_return(true) 11 | expect(File).to receive(:read).twice.and_return('%p hello') 12 | get '/existing_haml' 13 | expect(last_response.body).to eq "

hello

\n" 14 | end 15 | 16 | it 'passes to next middleware' do 17 | expect_any_instance_of(compiler).to receive(:view_exists?).and_return(false) 18 | get '/inexisting_haml' 19 | expect(last_response.body).to eq '

404

' 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware/helpers/format_of_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware::Helpers::FormatOf do 6 | let(:obj) { 7 | @obj = Object.new 8 | @obj.extend(Ki::Middleware::Helpers::FormatOf) 9 | } 10 | 11 | it 'should parse url format' do 12 | [nil, {}, '', '.json'].each do |s| 13 | expect(obj.format_of(s)).to eq '' 14 | end 15 | 16 | ['asd.json', 'asd.json?asd=1'].each do |s| 17 | expect(obj.format_of(s)).to eq 'json' 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware/init_middleware_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware::InitMiddleware do 6 | let(:app) { {} } 7 | let(:init) { Ki::Middleware::InitMiddleware.new app } 8 | let(:req) { Ki::BaseRequest } 9 | 10 | it 'redirects to /index if public/index.html does not exist' do 11 | env = Rack::MockRequest.env_for('/', { 'REQUEST_METHOD' => 'GET' }) 12 | resp = init.call env 13 | expect(resp[0]).to eq 302 # redirect 14 | end 15 | 16 | it 'renders index.html if it exists' do 17 | env = Rack::MockRequest.env_for('/', { 'REQUEST_METHOD' => 'GET' }) 18 | 19 | expect_any_instance_of(Ki::Middleware::InitMiddleware).to receive(:public_file_exists?).and_return(true) 20 | resp = init.call env 21 | expect(resp[0]).to eq 404 # not found because index.html doesn't exist 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware/insta_doc_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware::InstaDoc do 6 | it 'works' do 7 | get '/instadoc' 8 | expect(last_response.body).to include('Ki InstaDoc') 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/lib/ki/middleware_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Middleware do 6 | context Ki::Middleware::ApiHandler do 7 | it 'should only handle urls maped from objects that extend Ki::Model' do 8 | get '/asd.json' 9 | json 10 | expect(last_response.status).to eq 404 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/lib/ki/modules/model_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Model::ModelHelper do 6 | include Ki::Model::ModelHelper 7 | 8 | it 'has skipped params' do 9 | expect(skipped_params).to eq [] 10 | end 11 | 12 | # TODO: find out if this is a good idea 13 | it 'tests the request' do 14 | class TmpClassModelHelper 15 | def get? 16 | true 17 | end 18 | 19 | def post? 20 | true 21 | end 22 | 23 | def put? 24 | true 25 | end 26 | 27 | def delete? 28 | true 29 | end 30 | end 31 | 32 | @req = TmpClassModelHelper.new 33 | expect(post?).to be true 34 | expect(put?).to be true 35 | expect(delete?).to be true 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/lib/ki/modules/restrictions_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::Model::Restrictions do 6 | before :all do 7 | class DoubleRestricted < Ki::Model 8 | requires :foo, :cez 9 | requires :bar 10 | end 11 | end 12 | 13 | it 'merges multiple generic_restrictions' do 14 | expect(DoubleRestricted.required_attributes).to eq %i[foo bar cez].sort 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/lib/ki/utils/api_error_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki::ApiError do 6 | it 'knows about descendants' do 7 | expect(Ki::ApiError.descendants).to include(Ki::InvalidUrlError) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'simplecov' 4 | SimpleCov.start 5 | 6 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 7 | ENV['RACK_ENV'] = 'test' 8 | 9 | require 'rack/test' 10 | 11 | require 'ki' 12 | 13 | module Ki 14 | class KiConfig 15 | def config_file_path 16 | config_yml_path = 'spec/config.yml' 17 | config_yml_path += '.example' unless File.exist?('spec/config.yml') 18 | config_yml_path 19 | end 20 | end 21 | end 22 | 23 | Ki::KiConfig.instance.read 'test' 24 | Ki::Orm::Db.instance.establish_connection 25 | 26 | # http://dhartweg.roon.io/rspec-testing-for-a-json-api 27 | module Requests 28 | module JsonHelpers 29 | def json 30 | @json = JSON.parse(last_response.body) 31 | # TODO: find out if it is needed to convert to a hash with indifferent 32 | # access 33 | # @json = @json.with_indifferent_access if @json.class == Hash 34 | @json 35 | end 36 | end 37 | end 38 | 39 | module RackAppMethod 40 | include Rack::Test::Methods 41 | 42 | def app 43 | Ki::Ki.new 44 | end 45 | end 46 | 47 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 48 | RSpec.configure do |config| 49 | # config.treat_symbols_as_metadata_keys_with_true_values = true 50 | config.run_all_when_everything_filtered = true 51 | config.filter_run :focus 52 | 53 | config.include Requests::JsonHelpers 54 | config.include RackAppMethod 55 | 56 | # config.order = 'random' 57 | end 58 | -------------------------------------------------------------------------------- /spec/util_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe Ki do 6 | it 'should run in test env' do 7 | expect(Ki::Ki.environment).to eq 'test' 8 | end 9 | 10 | it 'should convert string to class corectly' do 11 | class Cez; end 12 | class CezBar; end 13 | expect('cez'.to_class).to eq Cez 14 | expect('cez_bar'.to_class).to eq CezBar 15 | end 16 | 17 | describe Array do 18 | it 'responds to present?' do 19 | expect([]).to_not be_present 20 | expect([1]).to be_present 21 | end 22 | end 23 | end 24 | --------------------------------------------------------------------------------