ki 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +12 -0
- data/Rakefile +34 -0
- data/VERSION +1 -0
- data/bin/ki +6 -0
- data/examples/base/Gemfile +5 -0
- data/examples/base/app.rb +5 -0
- data/examples/base/config.ru +2 -0
- data/examples/base/config.yml +3 -0
- data/examples/example/Gemfile +2 -0
- data/examples/example/app.rb +19 -0
- data/examples/example/config.ru +2 -0
- data/examples/example/config.yml +3 -0
- data/examples/example/public/custom.css +0 -0
- data/examples/example/public/custom.js +0 -0
- data/lib/cli.rb +43 -0
- data/lib/controller.rb +15 -0
- data/lib/db.rb +49 -0
- data/lib/ki.rb +41 -0
- data/lib/model.rb +128 -0
- data/lib/modules.rb +54 -0
- data/lib/public/bootstrap.css +6004 -0
- data/lib/public/bootstrap.js +2036 -0
- 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 +2 -0
- data/lib/public/ki.css +56 -0
- data/lib/request.rb +14 -0
- data/lib/response.rb +51 -0
- data/lib/util.rb +84 -0
- data/lib/views/container.haml +6 -0
- data/lib/views/footer.haml +2 -0
- data/lib/views/form.haml +10 -0
- data/lib/views/index.haml +19 -0
- data/lib/views/models.haml +14 -0
- data/lib/views/navbar.haml +11 -0
- data/spec/ki_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/util_spec.rb +25 -0
- metadata +256 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3@ki
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Cristian Mircea Messel
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "ki"
|
18
|
+
gem.homepage = "http://github.com/mess110/ki"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{one-line summary of your gem}
|
21
|
+
gem.description = %Q{longer description of your gem}
|
22
|
+
gem.email = "mess110@gmail.com"
|
23
|
+
gem.authors = ["Cristian Mircea Messel"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/ki
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'ki'
|
2
|
+
|
3
|
+
class Phone < Ki::Model
|
4
|
+
forbid :delete
|
5
|
+
requires :foo, :bar
|
6
|
+
end
|
7
|
+
|
8
|
+
class User < Ki::Model
|
9
|
+
requires :name, :password
|
10
|
+
respond_to :json
|
11
|
+
|
12
|
+
def before_create
|
13
|
+
@req.params[:password] = 'hashed password'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Post < Ki::Model
|
18
|
+
requires :text
|
19
|
+
end
|
File without changes
|
File without changes
|
data/lib/cli.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
|
4
|
+
class AppGenerator < Thor::Group
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
argument :app_name
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.join(File.dirname(__FILE__), '..', 'examples', 'base')
|
11
|
+
end
|
12
|
+
|
13
|
+
def dest_root
|
14
|
+
File.join(Dir.pwd, app_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepare_dir
|
18
|
+
if Dir.exists? dest_root
|
19
|
+
unless yes? "#{app_name} already exists. Do you want to overwrite it?"
|
20
|
+
say 'aborted'
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
else
|
24
|
+
Dir.mkdir dest_root
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_app
|
29
|
+
copy_file "Gemfile", "#{app_name}/Gemfile"
|
30
|
+
copy_file "config.ru", "#{app_name}/config.ru"
|
31
|
+
copy_file "config.yml", "#{app_name}/config.yml"
|
32
|
+
copy_file "app.rb", "#{app_name}/app.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Cli < Thor
|
37
|
+
register AppGenerator, :new, 'new', 'generate a new app'
|
38
|
+
|
39
|
+
desc 'hello [NAME]', 'says hello'
|
40
|
+
def hello(name)
|
41
|
+
say "Hello #{name}"
|
42
|
+
end
|
43
|
+
end
|
data/lib/controller.rb
ADDED
data/lib/db.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
require 'singleton'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Ki
|
6
|
+
class Db
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
attr_accessor :db
|
10
|
+
|
11
|
+
def all name
|
12
|
+
items = []
|
13
|
+
@db[name].find.each { |row| items << row }
|
14
|
+
items
|
15
|
+
end
|
16
|
+
|
17
|
+
def find name, id
|
18
|
+
item = @db[name].find(:_id => BSON::ObjectId(id))
|
19
|
+
item.to_a.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert name, hash
|
23
|
+
@db[name].insert(hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
# document doesn't update if id is in the hash
|
27
|
+
def update name, hash
|
28
|
+
id = hash['_id']
|
29
|
+
hash.delete('_id')
|
30
|
+
@db[name].update({:_id => BSON::ObjectId(id)}, hash)
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove name, id
|
34
|
+
@db[name].remove(:_id => BSON::ObjectId(id))
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear_model_subclasses
|
38
|
+
@db['ki_model_subclasses'].remove
|
39
|
+
end
|
40
|
+
|
41
|
+
def config host, port, db_name
|
42
|
+
@con = Mongo::Connection.new(host, port)
|
43
|
+
@db = @con.db(db_name)
|
44
|
+
rescue Mongo::ConnectionFailure
|
45
|
+
puts "Could not connect to MongoDB"
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/ki.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'haml'
|
3
|
+
|
4
|
+
require 'util'
|
5
|
+
require 'db'
|
6
|
+
require 'request'
|
7
|
+
require 'modules'
|
8
|
+
require 'model'
|
9
|
+
require 'controller'
|
10
|
+
require 'response'
|
11
|
+
|
12
|
+
Ki::Util.config_db
|
13
|
+
Ki::Db.instance.clear_model_subclasses
|
14
|
+
|
15
|
+
module Ki
|
16
|
+
class Ki
|
17
|
+
def self.new
|
18
|
+
Rack::Builder.new do
|
19
|
+
static_path = File.join(Ki.root, 'lib/public/*')
|
20
|
+
static_files = Dir.glob(static_path).collect{|c| File.basename(c)}
|
21
|
+
static_files.each do |sf|
|
22
|
+
map "/public/#{sf}" do
|
23
|
+
run Rack::File.new(Util::public_dir(sf))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
map '/' do
|
28
|
+
run Controller.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.root
|
34
|
+
File.join(File.expand_path(File.join(File.dirname(__FILE__), '..')))
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.app_name
|
38
|
+
File.basename(Dir.getwd)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/model.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
module Ki
|
2
|
+
class Model
|
3
|
+
extend Restrictions
|
4
|
+
include Callbacks
|
5
|
+
|
6
|
+
attr_reader :req
|
7
|
+
|
8
|
+
def initialize req
|
9
|
+
@req = req
|
10
|
+
@db = Db.instance
|
11
|
+
end
|
12
|
+
|
13
|
+
def forbid_actions
|
14
|
+
[]
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_to
|
18
|
+
[:json, :html]
|
19
|
+
end
|
20
|
+
|
21
|
+
def required_attributes
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
def format_response code, message, error=false
|
26
|
+
if message.class == String
|
27
|
+
message = { :message => message }
|
28
|
+
end
|
29
|
+
unless message.class == Array
|
30
|
+
message = [message]
|
31
|
+
end
|
32
|
+
|
33
|
+
# fix this hack
|
34
|
+
@req[:code] = code
|
35
|
+
|
36
|
+
return {
|
37
|
+
:code => code,
|
38
|
+
:message => message,
|
39
|
+
:format => @req.format
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO fix this method
|
44
|
+
def to_response
|
45
|
+
if self.class == Model
|
46
|
+
return format_response(404, 'did you get lost?')
|
47
|
+
end
|
48
|
+
|
49
|
+
unless respond_to.include? @req.format
|
50
|
+
return format_response(403, 'format not allowed')
|
51
|
+
end
|
52
|
+
|
53
|
+
unless route_allowed?
|
54
|
+
return format_response(403, 'not allowed')
|
55
|
+
end
|
56
|
+
|
57
|
+
case @req.request_method
|
58
|
+
when 'GET'
|
59
|
+
if @req.params['_id']
|
60
|
+
s = @db.find(@req.base, @req.params['_id'])
|
61
|
+
else
|
62
|
+
s = @db.all(@req.base)
|
63
|
+
end
|
64
|
+
|
65
|
+
return format_response(200, s)
|
66
|
+
when 'POST'
|
67
|
+
before_create
|
68
|
+
required_attributes.each do |ra|
|
69
|
+
unless @req.params.keys.include? ra.to_s
|
70
|
+
return format_response(400, "required attribute #{ra} missing")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
id = @db.insert(@req.base, @req.params)
|
74
|
+
after_create
|
75
|
+
return format_response(200, id)
|
76
|
+
when 'PUT'
|
77
|
+
if @req.params['_id']
|
78
|
+
required_attributes.each do |ra|
|
79
|
+
unless @req.params.keys.include? ra.to_s
|
80
|
+
return format_response(400, "required attribute #{ra} missing")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
before_update
|
85
|
+
result = @db.update(@req.base, @req.params)
|
86
|
+
after_update
|
87
|
+
return format_response(200, result)
|
88
|
+
else
|
89
|
+
return format_response(400, 'missing param _id')
|
90
|
+
end
|
91
|
+
when 'DELETE'
|
92
|
+
if @req.params['_id']
|
93
|
+
before_delete
|
94
|
+
result = @db.remove(@req.base, @req.params['_id'])
|
95
|
+
after_delete
|
96
|
+
return format_response(200, result)
|
97
|
+
else
|
98
|
+
return format_response(400, 'missing param _id')
|
99
|
+
end
|
100
|
+
else
|
101
|
+
return format_response(405, 'method not allowed')
|
102
|
+
end
|
103
|
+
rescue BSON::InvalidObjectId
|
104
|
+
return format_response(400, "invalid object _id")
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def route_allowed?
|
110
|
+
case @req.request_method
|
111
|
+
when 'GET'
|
112
|
+
if @req.params['_id']
|
113
|
+
!forbid_actions.include? :find
|
114
|
+
else
|
115
|
+
!forbid_actions.include? :find_all
|
116
|
+
end
|
117
|
+
when 'POST'
|
118
|
+
!forbid_actions.include? :create
|
119
|
+
when 'PUT'
|
120
|
+
!forbid_actions.include? :update
|
121
|
+
when 'DELETE'
|
122
|
+
!forbid_actions.include? :delete
|
123
|
+
else
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|