ki 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|