ki 0.3.3 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +1 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +2 -21
- data/Gemfile.lock +60 -100
- data/Guardfile +1 -1
- data/LICENSE.md +21 -0
- data/README.md +63 -0
- data/Rakefile +3 -45
- data/bin/ki +1 -1
- data/ki.gemspec +31 -137
- data/lib/ki.rb +21 -21
- data/lib/ki/api_error.rb +28 -0
- data/lib/ki/base_request.rb +34 -0
- data/lib/ki/helpers.rb +55 -0
- data/lib/ki/ki.rb +40 -0
- data/lib/ki/ki_cli.rb +46 -0
- data/lib/ki/ki_config.rb +20 -0
- data/lib/ki/middleware.rb +155 -0
- data/lib/ki/model.rb +70 -0
- data/lib/{modules → ki/modules}/callbacks.rb +0 -0
- data/lib/ki/modules/format_of.rb +10 -0
- data/lib/{modules → ki/modules}/model_helpers.rb +0 -12
- data/lib/ki/modules/public_file_helper.rb +13 -0
- data/lib/ki/modules/query_interface.rb +36 -0
- data/lib/{modules → ki/modules}/restrictions.rb +0 -30
- data/lib/ki/modules/view_helper.rb +13 -0
- data/lib/ki/orm.rb +65 -0
- data/lib/ki/version.rb +3 -0
- data/spec/examples/base/.ruby-gemset +1 -0
- data/spec/examples/base/.ruby-version +1 -0
- data/spec/examples/base/Gemfile +4 -0
- data/spec/examples/base/app.rb +1 -0
- data/{examples → spec/examples}/base/config.ru +0 -3
- data/spec/examples/base/config.yml +17 -0
- data/spec/examples/base/public/favicon.ico +0 -0
- data/spec/examples/base/public/javascripts/.gitkeep +0 -0
- data/spec/examples/base/public/stylesheets/.gitkeep +0 -0
- data/spec/examples/base/views/index.haml +6 -0
- data/spec/examples/couch-lock/.ruby-gemset +1 -0
- data/spec/examples/couch-lock/.ruby-version +1 -0
- data/spec/examples/couch-lock/Gemfile +4 -0
- data/spec/examples/couch-lock/Gemfile.lock +48 -0
- data/spec/examples/couch-lock/app.rb +42 -0
- data/spec/examples/couch-lock/config.ru +3 -0
- data/spec/examples/couch-lock/config.yml +17 -0
- data/spec/examples/couch-lock/install.sh +3 -0
- data/spec/examples/couch-lock/public/favicon.ico +0 -0
- data/spec/examples/couch-lock/public/javascripts/.gitkeep +0 -0
- data/spec/examples/couch-lock/public/stylesheets/.gitkeep +0 -0
- data/spec/examples/couch-lock/views/index.haml +18 -0
- data/spec/examples/json.northpole.ro/.ruby-gemset +1 -0
- data/spec/examples/json.northpole.ro/.ruby-version +1 -0
- data/spec/examples/json.northpole.ro/Capfile +25 -0
- data/spec/examples/json.northpole.ro/Gemfile +6 -0
- data/spec/examples/json.northpole.ro/Gemfile.lock +68 -0
- data/spec/examples/json.northpole.ro/app.rb +79 -0
- data/spec/examples/json.northpole.ro/client.rb +25 -0
- data/spec/examples/json.northpole.ro/config.ru +3 -0
- data/spec/examples/json.northpole.ro/config.yml +17 -0
- data/spec/examples/json.northpole.ro/config/deploy.rb +59 -0
- data/spec/examples/json.northpole.ro/config/deploy/production.rb +39 -0
- data/spec/examples/json.northpole.ro/foo.rb +10 -0
- data/spec/examples/json.northpole.ro/public/JNorthPole.jar +0 -0
- data/spec/examples/json.northpole.ro/public/favicon.ico +0 -0
- data/spec/examples/json.northpole.ro/public/font/FontAwesome.otf +0 -0
- data/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.eot +0 -0
- data/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.svg +284 -0
- data/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.ttf +0 -0
- data/spec/examples/json.northpole.ro/public/font/fontawesome-webfont.woff +0 -0
- data/spec/examples/json.northpole.ro/public/images/bear.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/bg.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/cloud.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/dotnet.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/dotnet_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/footer.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/glyphicons-halflings-white.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/glyphicons-halflings.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/ice.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/java.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/java_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/json.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/json_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/logo.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/mobile.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/php.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/php_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/python.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/python_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/ruby.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/ruby_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/shell.png +0 -0
- data/spec/examples/json.northpole.ro/public/images/shell_small.png +0 -0
- data/spec/examples/json.northpole.ro/public/javascripts/analytics.js +9 -0
- data/spec/examples/json.northpole.ro/public/javascripts/app.coffee +133 -0
- data/spec/examples/json.northpole.ro/public/javascripts/docs.min.js +16 -0
- data/spec/examples/json.northpole.ro/public/javascripts/jnorthpole.coffee +34 -0
- data/spec/examples/json.northpole.ro/public/javascripts/util.coffee +31 -0
- data/spec/examples/json.northpole.ro/public/stylesheets/app.sass +23 -0
- data/spec/examples/json.northpole.ro/public/stylesheets/application.sass +222 -0
- data/spec/examples/json.northpole.ro/public/stylesheets/cover.css +154 -0
- data/spec/examples/json.northpole.ro/views/curl.haml +19 -0
- data/spec/examples/json.northpole.ro/views/doc.haml +16 -0
- data/spec/examples/json.northpole.ro/views/doc_table.haml +45 -0
- data/spec/examples/json.northpole.ro/views/dotnet.haml +24 -0
- data/spec/examples/json.northpole.ro/views/examples.haml +28 -0
- data/spec/examples/json.northpole.ro/views/faq.haml +32 -0
- data/spec/examples/json.northpole.ro/views/foo.haml +7 -0
- data/spec/examples/json.northpole.ro/views/index.haml +4 -0
- data/spec/examples/json.northpole.ro/views/java.haml +0 -0
- data/spec/examples/json.northpole.ro/views/layout.haml +42 -0
- data/spec/examples/json.northpole.ro/views/php.haml +37 -0
- data/spec/examples/json.northpole.ro/views/playground.haml +38 -0
- data/spec/examples/json.northpole.ro/views/python.haml +17 -0
- data/spec/examples/json.northpole.ro/views/ruby.haml +12 -0
- data/spec/examples/json.northpole.ro/views/signup.haml +2 -0
- data/spec/examples/json.northpole.ro/views/thanks.haml +12 -0
- data/spec/functional_spec.rb +4 -0
- data/spec/lib/ki/base_request_spec.rb +88 -0
- data/spec/lib/ki/helpers_spec.rb +12 -0
- data/spec/lib/ki/ki_config_spec.rb +7 -0
- data/spec/lib/ki/middleware_spec.rb +11 -0
- data/spec/lib/ki/model_spec.rb +100 -0
- data/spec/lib/ki/modules/format_of_spec.rb +15 -0
- data/spec/lib/ki/orm_spec.rb +83 -0
- data/spec/spec_helper.rb +18 -5
- data/spec/util_spec.rb +7 -36
- metadata +269 -193
- data/.document +0 -5
- data/.rvmrc +0 -1
- data/LICENSE.txt +0 -20
- data/README.rdoc +0 -3
- data/VERSION +0 -1
- data/examples/base/Gemfile +0 -3
- data/examples/base/Gemfile.lock +0 -24
- data/examples/base/app.rb +0 -4
- data/examples/base/config.yml +0 -16
- data/gfx/colors.xcf +0 -0
- data/gfx/ki3.eps +0 -0
- data/gfx/logo.png +0 -0
- data/ki_wrap.rb +0 -35
- data/lib/conf.rb +0 -28
- data/lib/db.rb +0 -73
- data/lib/helpers.rb +0 -34
- data/lib/ki_cli.rb +0 -76
- data/lib/mockreq.rb +0 -45
- data/lib/model.rb +0 -110
- data/lib/modules/email.rb +0 -51
- data/lib/modules/query_interface.rb +0 -42
- data/lib/req.rb +0 -57
- data/lib/resp.rb +0 -70
- data/lib/static_file.rb +0 -68
- data/lib/util.rb +0 -114
- data/lib/views/404.haml +0 -1
- data/lib/views/406.haml +0 -1
- data/lib/views/index.haml +0 -7
- data/lib/views/layout.haml +0 -1
- data/spec/conf_spec.rb +0 -6
- data/spec/db_spec.rb +0 -93
- data/spec/integration/integration_spec.rb +0 -114
- data/spec/ki_spec.rb +0 -7
- data/spec/model_spec.rb +0 -91
- data/spec/modules/callbacks_spec.rb +0 -25
- data/spec/modules/email_spec.rb +0 -19
- data/spec/modules/query_interface_spec.rb +0 -93
- data/spec/modules/restrictions_spec.rb +0 -110
- data/spec/performance/performance.sh +0 -1
- data/spec/req_spec.rb +0 -29
data/lib/ki.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
require '
|
1
|
+
require 'yaml'
|
2
|
+
require 'uri'
|
2
3
|
|
4
|
+
require 'rack'
|
5
|
+
require 'rack/parser'
|
3
6
|
require 'haml'
|
4
7
|
require 'sass'
|
5
8
|
require 'coffee-script'
|
9
|
+
require 'mongo'
|
10
|
+
|
6
11
|
|
7
|
-
require '
|
8
|
-
require 'db'
|
9
|
-
require 'model'
|
10
|
-
require 'helpers'
|
11
|
-
require 'util'
|
12
|
-
require 'req'
|
13
|
-
require 'resp'
|
14
|
-
require 'static_file'
|
15
|
-
require 'mockreq'
|
12
|
+
require 'ki/api_error'
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
require 'ki/modules/query_interface'
|
15
|
+
require 'ki/modules/restrictions'
|
16
|
+
require 'ki/modules/callbacks'
|
17
|
+
require 'ki/modules/model_helpers'
|
18
|
+
require 'ki/modules/view_helper'
|
19
|
+
require 'ki/modules/format_of'
|
20
|
+
require 'ki/modules/public_file_helper'
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
require 'ki/ki_config'
|
23
|
+
require 'ki/helpers'
|
24
|
+
require 'ki/orm'
|
25
|
+
require 'ki/ki'
|
26
|
+
require 'ki/model'
|
27
|
+
require 'ki/middleware'
|
28
|
+
require 'ki/base_request'
|
data/lib/ki/api_error.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ki
|
2
|
+
class ApiError < StandardError
|
3
|
+
attr_reader :status
|
4
|
+
|
5
|
+
def initialize body, status=400
|
6
|
+
super body
|
7
|
+
@status = status
|
8
|
+
end
|
9
|
+
|
10
|
+
def result
|
11
|
+
{ 'error' => to_s }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidUrlError < ApiError; end
|
16
|
+
class RequiredAttributeMissing < ApiError; end
|
17
|
+
class AttributeNotUnique < ApiError; end
|
18
|
+
class ForbiddenAction < ApiError
|
19
|
+
def initialize
|
20
|
+
super 'action forbidden', 400
|
21
|
+
end
|
22
|
+
end
|
23
|
+
class PartialNotFoundError < ApiError
|
24
|
+
def initialize s
|
25
|
+
super "partial #{s} not found", 404
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Ki
|
2
|
+
class BaseRequest < Rack::Request
|
3
|
+
include FormatOf
|
4
|
+
|
5
|
+
def root?
|
6
|
+
path == '/'
|
7
|
+
end
|
8
|
+
|
9
|
+
def json?
|
10
|
+
content_type == 'application/json' || format_of(path) == 'json'
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ki_model_class
|
14
|
+
self.path.to_s.gsub('/','').gsub(format_of(path), '').gsub('.', '').to_class
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_action
|
18
|
+
case request_method
|
19
|
+
when 'GET'
|
20
|
+
:find
|
21
|
+
when 'POST'
|
22
|
+
:create
|
23
|
+
when 'PUT'
|
24
|
+
:update
|
25
|
+
when 'DELETE'
|
26
|
+
:delete
|
27
|
+
when 'SEARCH'
|
28
|
+
:find
|
29
|
+
else
|
30
|
+
raise 'unkown action'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/ki/helpers.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
class String
|
2
|
+
def to_class
|
3
|
+
chain = self.split "::"
|
4
|
+
klass = Kernel
|
5
|
+
chain.each do |klass_string|
|
6
|
+
klass = klass.const_get klass_string.capitalize
|
7
|
+
end
|
8
|
+
klass.is_a?(Class) ? klass : nil
|
9
|
+
rescue NameError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Array
|
15
|
+
def stringify_ids
|
16
|
+
self.collect do |e|
|
17
|
+
if e['_id']
|
18
|
+
e['id'] = e['_id'].to_s
|
19
|
+
e.delete('_id')
|
20
|
+
end
|
21
|
+
if e[:_id]
|
22
|
+
e['id'] = e[:_id].to_s
|
23
|
+
e.delete(:_id)
|
24
|
+
end
|
25
|
+
e
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Ki
|
31
|
+
module Helpers
|
32
|
+
include ViewHelper
|
33
|
+
|
34
|
+
def css url
|
35
|
+
render_haml "%link{:href => '#{url}', :rel => 'stylesheet'}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def js url
|
39
|
+
render_haml "%script{:src => '#{url}'}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_haml s
|
43
|
+
Haml::Engine.new(s).render
|
44
|
+
end
|
45
|
+
|
46
|
+
def partial s
|
47
|
+
path = view_path(s)
|
48
|
+
if File.file?(path)
|
49
|
+
render_haml(File.read(path))
|
50
|
+
else
|
51
|
+
raise PartialNotFoundError.new path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/ki/ki.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ki
|
2
|
+
class KiApp
|
3
|
+
# should never reach this
|
4
|
+
# middleware should catch all the requests
|
5
|
+
def call env
|
6
|
+
s = 'misplaced in space'
|
7
|
+
Rack::Response.new(s).finish
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Ki
|
12
|
+
PUBLIC_PATH = 'public'
|
13
|
+
VIEWS_PATH = 'views'
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
KiConfig.instance.read environment
|
17
|
+
Orm::Db.instance.establish_connection
|
18
|
+
|
19
|
+
@app = Rack::Builder.new do
|
20
|
+
use Middleware::InitMiddleware
|
21
|
+
# TODO what happens with invalid json?
|
22
|
+
use Rack::Parser, :content_types => { 'application/json' => Proc.new { |body| ::MultiJson.decode body } }
|
23
|
+
use Middleware::ApiHandler
|
24
|
+
use Middleware::CoffeeCompiler
|
25
|
+
use Middleware::SassCompiler
|
26
|
+
use Middleware::HamlCompiler
|
27
|
+
use Middleware::PublicFileServer
|
28
|
+
run KiApp.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def environment
|
33
|
+
ENV['RACK_ENV']
|
34
|
+
end
|
35
|
+
|
36
|
+
def call env
|
37
|
+
@app.call env
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/ki/ki_cli.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
class KiGenerator < Thor::Group
|
4
|
+
include Thor::Actions
|
5
|
+
|
6
|
+
def self.source_root
|
7
|
+
File.join(File.dirname(__FILE__), '..', '..')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class AppGenerator < KiGenerator
|
12
|
+
|
13
|
+
argument :app_name
|
14
|
+
|
15
|
+
def prepare_dir
|
16
|
+
unless app_name =~ /^[a-zA-Z0-9-]*$/
|
17
|
+
say "App name must contain only alphanumeric characters and -"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
21
|
+
if Dir.exists? app_name
|
22
|
+
say "#{app_name} already exists"
|
23
|
+
exit 2
|
24
|
+
end
|
25
|
+
|
26
|
+
Dir.mkdir app_name
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_app
|
30
|
+
directory("spec/examples/base", app_name)
|
31
|
+
|
32
|
+
# Set database names
|
33
|
+
config_file = File.read("#{app_name}/config.yml")
|
34
|
+
config_file.gsub!("name: np_development", "name: #{app_name}_development")
|
35
|
+
config_file.gsub!("name: np_test", "name: #{app_name}_test")
|
36
|
+
config_file.gsub!("name: np", "name: #{app_name}")
|
37
|
+
File.open("#{app_name}/config.yml", "w") {|file| file.puts config_file}
|
38
|
+
|
39
|
+
# Set rvm gemset name
|
40
|
+
`echo #{app_name} > #{app_name}/.ruby-gemset`
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class KiCli < Thor
|
45
|
+
register AppGenerator, :new, 'new [APP_NAME]', 'generate a new app'
|
46
|
+
end
|
data/lib/ki/ki_config.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Ki
|
4
|
+
class KiConfig
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
CONFIG_FILE_PATH = 'config.yml'
|
8
|
+
|
9
|
+
attr_reader :config, :environment
|
10
|
+
|
11
|
+
def read environment
|
12
|
+
@environment = environment
|
13
|
+
@config = YAML.load_file(CONFIG_FILE_PATH)[environment]
|
14
|
+
end
|
15
|
+
|
16
|
+
def database
|
17
|
+
@config['database']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module Ki
|
2
|
+
module Middleware
|
3
|
+
module BaseMiddleware
|
4
|
+
include FormatOf
|
5
|
+
include ViewHelper
|
6
|
+
include PublicFileHelper
|
7
|
+
|
8
|
+
def initialize app
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class PublicFileServer
|
14
|
+
include BaseMiddleware
|
15
|
+
|
16
|
+
def call env
|
17
|
+
req = BaseRequest.new env
|
18
|
+
if public_file_exists? req
|
19
|
+
Rack::File.new(Ki::PUBLIC_PATH).call env
|
20
|
+
else
|
21
|
+
@app.call env
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class InitMiddleware
|
27
|
+
include BaseMiddleware
|
28
|
+
|
29
|
+
def call env
|
30
|
+
req = BaseRequest.new env
|
31
|
+
if req.root?
|
32
|
+
resp = Rack::Response.new
|
33
|
+
resp.redirect('/index')
|
34
|
+
resp.finish
|
35
|
+
else
|
36
|
+
env['CONTENT_TYPE'] = 'application/json' if format_of(req) == 'json'
|
37
|
+
@app.call env
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class HamlCompiler
|
43
|
+
include BaseMiddleware
|
44
|
+
|
45
|
+
def call env
|
46
|
+
req = BaseRequest.new env
|
47
|
+
if view_exists?(req)
|
48
|
+
render_haml view_path(req)
|
49
|
+
else
|
50
|
+
@app.call env
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def render_haml file_path
|
55
|
+
file_contents = File.read(file_path)
|
56
|
+
|
57
|
+
if view_exists? 'layout'
|
58
|
+
layout_contents = File.read(view_path('layout'))
|
59
|
+
else
|
60
|
+
layout_contents = "= yield"
|
61
|
+
end
|
62
|
+
|
63
|
+
html = haml(layout_contents).render do
|
64
|
+
haml(file_contents).render
|
65
|
+
end
|
66
|
+
|
67
|
+
Rack::Response.new(html).finish
|
68
|
+
end
|
69
|
+
|
70
|
+
def haml s
|
71
|
+
Haml::Engine.new("- extend Ki::Helpers\n" + s)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class CoffeeCompiler
|
76
|
+
include BaseMiddleware
|
77
|
+
|
78
|
+
def call env
|
79
|
+
req = BaseRequest.new env
|
80
|
+
coffee_path = req.path.to_s[0...-3] + '.coffee'
|
81
|
+
if !public_file_exists?(req) && format_of(req) == 'js' && public_file_exists?(coffee_path)
|
82
|
+
js = CoffeeScript.compile(File.read(public_file_path(coffee_path)))
|
83
|
+
Rack::Response.new(js).finish
|
84
|
+
else
|
85
|
+
@app.call env
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class SassCompiler
|
91
|
+
include BaseMiddleware
|
92
|
+
|
93
|
+
def call env
|
94
|
+
req = BaseRequest.new env
|
95
|
+
sass_path = req.path.to_s[0...-4] + '.sass'
|
96
|
+
# if req ends with css and it does not exist, if a sass file exists instead
|
97
|
+
if !public_file_exists?(req) && format_of(req) == 'css' && public_file_exists?(sass_path)
|
98
|
+
eng = Sass::Engine.new(File.read(public_file_path(sass_path)), :syntax => :sass)
|
99
|
+
Rack::Response.new(eng.render).finish
|
100
|
+
else
|
101
|
+
@app.call env
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Handles all API calls
|
107
|
+
#
|
108
|
+
# Any json request is considered an api call. A request is considered as json
|
109
|
+
# if the format is .json or Content-Type header is set to 'application/json'
|
110
|
+
#
|
111
|
+
# If the query param 'redirect_to' is given, the response will not contain the
|
112
|
+
# json output from the url, instead it will redirect to the url given
|
113
|
+
class ApiHandler
|
114
|
+
include BaseMiddleware
|
115
|
+
|
116
|
+
def call env
|
117
|
+
req = BaseRequest.new env
|
118
|
+
if req.json?
|
119
|
+
begin
|
120
|
+
klass = req.to_ki_model_class
|
121
|
+
if Model.descendants.include? klass
|
122
|
+
# TODO do not have redirect_to param
|
123
|
+
model = klass.new(req.to_action, req.params)
|
124
|
+
# TODO document this
|
125
|
+
if req.params['redirect_to'].nil?
|
126
|
+
render model
|
127
|
+
else
|
128
|
+
# TODO check for injection
|
129
|
+
redirect_to req.params['redirect_to']
|
130
|
+
end
|
131
|
+
else
|
132
|
+
raise InvalidUrlError.new("invalid url '#{req.path}'", 404)
|
133
|
+
end
|
134
|
+
rescue ApiError => e
|
135
|
+
render e
|
136
|
+
end
|
137
|
+
else
|
138
|
+
@app.call env
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def redirect_to s
|
143
|
+
resp = Rack::Response.new
|
144
|
+
resp.redirect(s)
|
145
|
+
resp.finish
|
146
|
+
end
|
147
|
+
|
148
|
+
def render r
|
149
|
+
resp = Rack::Response.new(r.result.to_json, r.status)
|
150
|
+
resp['Content-Type'] = 'application/json'
|
151
|
+
resp.finish
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/ki/model.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Ki
|
2
|
+
class Model
|
3
|
+
extend QueryInterface
|
4
|
+
extend Restrictions
|
5
|
+
include Callbacks
|
6
|
+
include ModelHelpers
|
7
|
+
|
8
|
+
attr_accessor :action, :result, :params, :status
|
9
|
+
|
10
|
+
def initialize action, params
|
11
|
+
@action = action
|
12
|
+
@params = params
|
13
|
+
@status = 200
|
14
|
+
|
15
|
+
raise ForbiddenAction.new if forbidden_actions.include? @action
|
16
|
+
ccall
|
17
|
+
end
|
18
|
+
|
19
|
+
def find
|
20
|
+
@result = self.class.find @params
|
21
|
+
end
|
22
|
+
|
23
|
+
def create
|
24
|
+
check_for_required_attributes
|
25
|
+
check_for_unique_attributes
|
26
|
+
@result = self.class.create @params
|
27
|
+
end
|
28
|
+
|
29
|
+
def update
|
30
|
+
check_for_required_attributes
|
31
|
+
check_for_unique_attributes
|
32
|
+
@result = self.class.update @params
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete
|
36
|
+
@result = self.class.delete @params
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def check_for_required_attributes
|
42
|
+
required_attributes.each do |ra|
|
43
|
+
if !@params.keys.include?(ra.to_s)
|
44
|
+
raise RequiredAttributeMissing.new("#{ra.to_s} missing")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_for_unique_attributes
|
50
|
+
unique_attributes.each do |ua|
|
51
|
+
u = self.class.find({ua.to_s => @params[ua.to_s]})
|
52
|
+
unless u.empty?
|
53
|
+
raise AttributeNotUnique.new("#{ua.to_s} not unique")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def ccall
|
59
|
+
before_all
|
60
|
+
send "before_#{@action.to_s}".to_sym
|
61
|
+
send @action.to_sym
|
62
|
+
send "after_#{@action.to_s}".to_sym
|
63
|
+
after_all
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.descendants
|
67
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|