ki 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -1
- data/Gemfile +6 -3
- data/Gemfile.lock +81 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +1 -1
- data/README.rdoc +8 -1
- data/Rakefile +17 -3
- data/VERSION +1 -1
- data/bin/ki +3 -2
- data/examples/base/Gemfile +1 -7
- data/examples/base/Gemfile.lock +24 -0
- data/examples/base/app.rb +1 -2
- data/examples/base/config.ru +4 -0
- data/examples/{example → northpole}/Gemfile +0 -1
- data/examples/northpole/Gemfile.lock +33 -0
- data/examples/northpole/app.rb +19 -0
- data/examples/northpole/config.ru +6 -0
- data/examples/northpole/config.yml +3 -0
- data/examples/northpole/views/index.haml +1 -0
- data/examples/northpole/views/website.haml +1 -0
- data/ki.gemspec +125 -0
- data/lib/db.rb +43 -13
- data/lib/extensions/mail.rb +41 -0
- data/lib/helpers.rb +16 -0
- data/lib/ki.rb +8 -27
- data/lib/ki_cli.rb +63 -0
- data/lib/model.rb +59 -92
- data/lib/modules/callbacks.rb +27 -0
- data/lib/modules/query_interface.rb +29 -0
- data/lib/modules/restrictions.rb +62 -0
- data/lib/req.rb +30 -0
- data/lib/resp.rb +50 -0
- data/lib/static_file.rb +14 -0
- data/lib/util.rb +21 -30
- data/lib/views/404.haml +7 -0
- data/lib/views/406.haml +7 -0
- data/lib/views/index.haml +5 -17
- data/logo.png +0 -0
- data/spec/db_spec.rb +71 -0
- data/spec/ki_spec.rb +2 -2
- data/spec/model_spec.rb +82 -0
- data/spec/modules/callbacks_spec.rb +14 -0
- data/spec/modules/query_interface_spec.rb +63 -0
- data/spec/modules/restrictions_spec.rb +64 -0
- data/spec/req_spec.rb +4 -0
- data/spec/spec_helper.rb +30 -1
- data/spec/util_spec.rb +32 -16
- metadata +89 -36
- data/examples/base/config.yml +0 -3
- data/examples/example/app.rb +0 -15
- data/examples/example/config.ru +0 -2
- data/examples/example/config.yml +0 -3
- data/examples/example/public/custom.css +0 -0
- data/examples/example/public/custom.js +0 -0
- data/lib/cli.rb +0 -51
- data/lib/controller.rb +0 -15
- data/lib/modules.rb +0 -54
- data/lib/public/bootstrap.css +0 -6004
- data/lib/public/bootstrap.js +0 -2036
- data/lib/public/custom.css +0 -0
- data/lib/public/custom.js +0 -0
- data/lib/public/glyphicons-halflings-white.png +0 -0
- data/lib/public/glyphicons-halflings.png +0 -0
- data/lib/public/jquery-1.8.2.min.js +0 -2
- data/lib/public/ki.css +0 -56
- data/lib/request.rb +0 -14
- data/lib/response.rb +0 -51
- data/lib/views/container.haml +0 -6
- data/lib/views/footer.haml +0 -2
- data/lib/views/form.haml +0 -10
- data/lib/views/models.haml +0 -14
- data/lib/views/navbar.haml +0 -11
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ki
|
2
|
+
module Callbacks
|
3
|
+
def before_find
|
4
|
+
end
|
5
|
+
|
6
|
+
def after_find
|
7
|
+
end
|
8
|
+
|
9
|
+
def before_create
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_create
|
13
|
+
end
|
14
|
+
|
15
|
+
def before_update
|
16
|
+
end
|
17
|
+
|
18
|
+
def after_update
|
19
|
+
end
|
20
|
+
|
21
|
+
def before_delete
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_delete
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Ki
|
2
|
+
module QueryInterface
|
3
|
+
def count
|
4
|
+
Db.instance.db[class_name].count
|
5
|
+
end
|
6
|
+
|
7
|
+
def find id
|
8
|
+
Db.instance.find class_name, id
|
9
|
+
end
|
10
|
+
|
11
|
+
def create hash
|
12
|
+
Db.instance.create class_name, hash
|
13
|
+
end
|
14
|
+
|
15
|
+
def update hash
|
16
|
+
Db.instance.update class_name, hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete id
|
20
|
+
Db.instance.delete class_name, id
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def class_name
|
26
|
+
self.to_s.downcase
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Ki
|
2
|
+
module Restrictions
|
3
|
+
def forbidden_actions
|
4
|
+
[]
|
5
|
+
end
|
6
|
+
|
7
|
+
def forbid *actions
|
8
|
+
send :define_method, :forbidden_actions do
|
9
|
+
actions
|
10
|
+
end
|
11
|
+
|
12
|
+
eigen_class = class << self; self; end
|
13
|
+
eigen_class.send(:define_method, :forbidden_actions) do
|
14
|
+
actions
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def allowed_formats
|
19
|
+
[:json, :html]
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to *formats
|
23
|
+
send :define_method, :allowed_formats do
|
24
|
+
formats
|
25
|
+
end
|
26
|
+
|
27
|
+
eigen_class = class << self; self; end
|
28
|
+
eigen_class.send(:define_method, :allowed_formats) do
|
29
|
+
formats
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def required_attributes
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
37
|
+
def requires *attributes
|
38
|
+
send :define_method, :required_attributes do
|
39
|
+
attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
eigen_class = class << self; self; end
|
43
|
+
eigen_class.send(:define_method, :required_attributes) do
|
44
|
+
attributes
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module InitRestrictions
|
50
|
+
def forbidden_actions
|
51
|
+
[]
|
52
|
+
end
|
53
|
+
|
54
|
+
def allowed_formats
|
55
|
+
[:json, :html]
|
56
|
+
end
|
57
|
+
|
58
|
+
def required_attributes
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/req.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Ki
|
2
|
+
class Req < Rack::Request
|
3
|
+
attr_accessor :code, :klass, :format
|
4
|
+
|
5
|
+
def initialize env
|
6
|
+
super env
|
7
|
+
end
|
8
|
+
|
9
|
+
def serve
|
10
|
+
unless static_path_requested?
|
11
|
+
route
|
12
|
+
Resp.new(self).finish
|
13
|
+
else
|
14
|
+
StaticFile.new(self).finish
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def route
|
19
|
+
@klass = Util.base(path).to_class
|
20
|
+
@format = Util.format(path)
|
21
|
+
@code = klass || path == '/' ? 200 : 404
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def static_path_requested?
|
27
|
+
path.start_with? '/public/'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/resp.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Ki
|
4
|
+
class Resp < Rack::Response
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
attr_accessor :req
|
8
|
+
|
9
|
+
def initialize req
|
10
|
+
@req = req
|
11
|
+
headers = {
|
12
|
+
'Content-Type' => respond_to(@req.format)
|
13
|
+
}
|
14
|
+
|
15
|
+
klass = @req.klass
|
16
|
+
if klass.allowed_formats.include? @req.format
|
17
|
+
if @req.code == 404
|
18
|
+
body = @req.format == :html ? partial('404') : { :error => 404 }
|
19
|
+
else
|
20
|
+
if @req.path == '/'
|
21
|
+
body = @req.format == :html ? partial('index') : { :index => 200 }
|
22
|
+
else
|
23
|
+
klass_view = Util.root_view_path(klass.to_s.downcase)
|
24
|
+
if @req.format == :html
|
25
|
+
body = File.exists?(klass_view) ? partial(klass.to_s.downcase) : partial('index')
|
26
|
+
else
|
27
|
+
begin
|
28
|
+
body = klass.new(@req).to_json
|
29
|
+
rescue Exception => e
|
30
|
+
@req.code = 400
|
31
|
+
body = { :error => e.to_s }.to_json
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
else
|
37
|
+
@req.code = 406
|
38
|
+
body = @req.format == :html ? partial('406') : { :error => 'format not allowed' }.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
super [body], @req.code, headers
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def respond_to resp
|
47
|
+
resp == :html ? 'text/html' : 'application/json'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/static_file.rb
ADDED
data/lib/util.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
class NilClass
|
2
|
+
def allowed_formats
|
3
|
+
[:json, :html]
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
1
7
|
class String
|
2
8
|
def to_class
|
3
9
|
chain = self.split "::"
|
@@ -13,22 +19,6 @@ end
|
|
13
19
|
|
14
20
|
module Ki
|
15
21
|
class Util
|
16
|
-
# the order in which files are loaded is important
|
17
|
-
def self.static_files
|
18
|
-
static_files = Dir.glob(File.join(Ki.root, 'lib/public/*'))
|
19
|
-
static_files << Dir.glob(File.join(Dir.pwd, 'public/*'))
|
20
|
-
static_files.flatten!
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.partial s, hash={}
|
24
|
-
path = File.join(Dir.pwd, 'views', s) + '.haml'
|
25
|
-
unless File.exists? path
|
26
|
-
path = File.join(Ki.root, 'lib', 'views', s) + '.haml'
|
27
|
-
end
|
28
|
-
contents = File.read(path)
|
29
|
-
Haml::Engine.new(contents).render(Object.new(), hash)
|
30
|
-
end
|
31
|
-
|
32
22
|
def self.base s
|
33
23
|
if s == '/'
|
34
24
|
return ''
|
@@ -52,19 +42,8 @@ module Ki
|
|
52
42
|
end
|
53
43
|
end
|
54
44
|
|
55
|
-
def self.
|
56
|
-
|
57
|
-
yaml = {}
|
58
|
-
if File.exists? config_path
|
59
|
-
yaml = YAML.load_file(config_path)
|
60
|
-
else
|
61
|
-
yaml = {
|
62
|
-
'host' => '127.0.0.1',
|
63
|
-
'port' => 27017,
|
64
|
-
'db_name' => 'ki'
|
65
|
-
}
|
66
|
-
end
|
67
|
-
Db.instance.config yaml['host'], yaml['port'], yaml['db_name']
|
45
|
+
def self.root_view_path name
|
46
|
+
File.join(root, 'views', name) + '.haml'
|
68
47
|
end
|
69
48
|
|
70
49
|
def self.format s
|
@@ -74,10 +53,22 @@ module Ki
|
|
74
53
|
|
75
54
|
base = s.split('/')[1]
|
76
55
|
if base.include? '.'
|
77
|
-
base.split('.')[1].split('?')[0].to_sym
|
56
|
+
base.split('.')[1].split('?')[0].to_sym rescue :html
|
78
57
|
else
|
79
58
|
:html
|
80
59
|
end
|
81
60
|
end
|
61
|
+
|
62
|
+
def self.src_path
|
63
|
+
File.join(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.root
|
67
|
+
Dir.getwd
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.app_name
|
71
|
+
File.basename(root)
|
72
|
+
end
|
82
73
|
end
|
83
74
|
end
|
data/lib/views/404.haml
ADDED
data/lib/views/406.haml
ADDED
data/lib/views/index.haml
CHANGED
@@ -1,19 +1,7 @@
|
|
1
|
-
!!!
|
2
|
-
%html
|
1
|
+
!!!
|
2
|
+
%html
|
3
3
|
%head
|
4
|
-
%
|
5
|
-
%
|
6
|
-
%meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
|
7
|
-
%meta{:content => "", :name => "description"}
|
8
|
-
%meta{:content => "", :name => "author"}
|
9
|
-
|
10
|
-
%link{:href => "/public/bootstrap.css", :rel => "stylesheet"}
|
11
|
-
%link{:href => "/public/ki.css", :rel => "stylesheet"}
|
12
|
-
%link{:href => "/public/custom.css", :rel => "stylesheet"}
|
4
|
+
%title 200 - Hello World
|
5
|
+
%meta{"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
|
13
6
|
%body
|
14
|
-
|
15
|
-
= Ki::Util.partial 'container', locals
|
16
|
-
|
17
|
-
%script{:src => "/public/jquery-1.8.2.min.js"}
|
18
|
-
%script{:src => "/public/bootstrap.js"}
|
19
|
-
%script{:src => "/public/custom.js"}
|
7
|
+
%center 200 - Hello World
|
data/logo.png
ADDED
Binary file
|
data/spec/db_spec.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Ki::Db do
|
4
|
+
before :each do
|
5
|
+
@db = Ki::Db.instance
|
6
|
+
@db.remove_collections
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should start with an empty database" do
|
10
|
+
@db.collection_names.empty?.should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should create a collection" do
|
14
|
+
@db.collection_names.empty?.should be_true
|
15
|
+
@db.create 'zoidberg', { :foo => 'bar' }
|
16
|
+
@db.collection_names.empty?.should be_false
|
17
|
+
@db.collection_names.include?('zoidberg').should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should create an model" do
|
21
|
+
@db.db['zoidberg'].count.should == 0
|
22
|
+
@db.create 'zoidberg', { :foo => [1,2] }
|
23
|
+
@db.db['zoidberg'].count.should == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return doc id when creating a model" do
|
27
|
+
post_id = @db.create 'zoidberg', { :foo => [1,2] }
|
28
|
+
post_id.class.should == String
|
29
|
+
bson_id = BSON::ObjectId.from_string(post_id)
|
30
|
+
@db.db['zoidberg'].find('_id' => bson_id).first['foo'].should == [1,2]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should find a specific document" do
|
34
|
+
post_id = @db.create 'zoidberg', { :foo => [1,2] }
|
35
|
+
@db.find('zoidberg', post_id)['foo'].should == [1,2]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should update a document" do
|
39
|
+
post_id = @db.create 'zoidberg', { :foo => [1,2], :bar => 'highj' }
|
40
|
+
@db.update 'zoidberg', { '_id' => post_id, :foo => 3.1415 }
|
41
|
+
doc = @db.find('zoidberg', post_id)
|
42
|
+
doc['foo'].should == 3.1415
|
43
|
+
doc['bar'].should == nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should delete a document" do
|
47
|
+
post_id = @db.create 'zoidberg', { :foo => [1,2], :bar => 'highj' }
|
48
|
+
@db.db['zoidberg'].count.should == 1
|
49
|
+
@db.delete('zoidberg', post_id)
|
50
|
+
@db.db['zoidberg'].count.should == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should find all documents in collection" do
|
54
|
+
@db.create 'zoidberg', { :foo => [1,2], :bar => 'highj' }
|
55
|
+
@db.create 'zoidberg', { :foo => [3,4], :bar => 'lowj' }
|
56
|
+
@db.find_all('zoidberg').count.should == @db.db['zoidberg'].count
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should find by an attribute" do
|
60
|
+
@db.create 'zoidberg', { :foo => [1,2], :bar => 'highj' }
|
61
|
+
@db.create 'zoidberg', { :foo => [3,4], :bar => 'lowj' }
|
62
|
+
@db.create 'zoidberg', { :foo => [5,6], :bar => 'lowj' }
|
63
|
+
r = @db.find_by 'zoidberg', { :bar => 'lowj' }
|
64
|
+
r.class.should == Array
|
65
|
+
r.length.should == 2
|
66
|
+
|
67
|
+
r = @db.find_by 'zoidberg', { :bar => 'lowj', :foo => [3,4] }
|
68
|
+
r.class.should == Array
|
69
|
+
r.length.should == 1
|
70
|
+
end
|
71
|
+
end
|
data/spec/ki_spec.rb
CHANGED
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Ki::Model do
|
4
|
+
before :each do
|
5
|
+
class Zoidberg < Ki::Model
|
6
|
+
end
|
7
|
+
@db = Ki::Db.instance
|
8
|
+
@db.remove_collections
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should correctly extract class_name" do
|
12
|
+
# Zoidberg.new(@get).class_name.should == "zoidberg"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should create a new item in the databse" do
|
16
|
+
post = ReqFactory.new(:post_homer)
|
17
|
+
@db.db['zoidberg'].count.should == 0
|
18
|
+
hash = Zoidberg.new(post).hash
|
19
|
+
@db.db['zoidberg'].count.should == 1
|
20
|
+
@db.find('zoidberg', hash['_id'])['user'].should == 'homer'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should get a model depending on id" do
|
24
|
+
id = @db.create 'zoidberg', { :name => 'homer' }
|
25
|
+
|
26
|
+
get = ReqFactory.new(:get, {'_id' => id})
|
27
|
+
Zoidberg.new(get).hash['name'].should == 'homer'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise error if no params are given when getting model" do
|
31
|
+
expect {
|
32
|
+
Zoidberg.new(ReqFactory.new(:get))
|
33
|
+
}.to raise_error
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise error if no id is given when getting model" do
|
37
|
+
expect {
|
38
|
+
Zoidberg.new(ReqFactory.new(:get_with_params, { :name => 'homer' }))
|
39
|
+
}.to raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should only accept valid object ids" do
|
43
|
+
expect {
|
44
|
+
Zoidberg.new(ReqFactory.new(:get_with_params, { :_id => 'homer' }))
|
45
|
+
}.to raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should update a document" do
|
49
|
+
id = @db.create 'zoidberg', { :name => 'homer' }
|
50
|
+
@db.find('zoidberg', id)['name'].should == 'homer'
|
51
|
+
|
52
|
+
Zoidberg.new(ReqFactory.new(:put, {'_id' => id, :name => 'bart'}))
|
53
|
+
@db.find('zoidberg', id)['name'].should == 'bart'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should raise error if id is missing when updating" do
|
57
|
+
expect {
|
58
|
+
Zoidberg.new(ReqFactory.new(:put, {:name => 'bart'}))
|
59
|
+
}.to raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should raise error if id is invalid" do
|
63
|
+
expect {
|
64
|
+
Zoidberg.new(ReqFactory.new(:put, {'_id' => 'f*ck', :name => 'bart'}))
|
65
|
+
}.to raise_error
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should delete a document" do
|
69
|
+
expect {
|
70
|
+
Zoidberg.new(ReqFactory.new(:delete))
|
71
|
+
}.to raise_error
|
72
|
+
|
73
|
+
expect {
|
74
|
+
Zoidberg.new(ReqFactory.new(:delete, {'_id' => 'f*ck', :name => 'bart'}))
|
75
|
+
}.to raise_error
|
76
|
+
|
77
|
+
id = @db.create 'zoidberg', { :name => 'homer' }
|
78
|
+
expect {
|
79
|
+
Zoidberg.new(ReqFactory.new(:delete, {'_id' => id}))
|
80
|
+
}.to change(@db.db['zoidberg'], :count).by(-1)
|
81
|
+
end
|
82
|
+
end
|