ki 0.4.11 → 0.4.12
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +9 -0
- data/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/.travis.yml +10 -2
- data/Gemfile +2 -0
- data/Gemfile.lock +95 -85
- data/Guardfile +20 -14
- data/MIDDLEWARE.md +2 -2
- data/README.md +29 -1
- data/Rakefile +3 -1
- data/bin/ki +1 -0
- data/ki.gemspec +15 -14
- data/lib/ki.rb +8 -1
- data/lib/ki/base_request.rb +7 -5
- data/lib/ki/channel_manager.rb +2 -0
- data/lib/ki/helpers.rb +4 -5
- data/lib/ki/ki.rb +11 -2
- data/lib/ki/ki_app.rb +2 -0
- data/lib/ki/ki_cli.rb +9 -2
- data/lib/ki/ki_config.rb +5 -2
- data/lib/ki/ki_logger.rb +15 -0
- data/lib/ki/middleware/admin_interface_generator.rb +3 -1
- data/lib/ki/middleware/api_handler.rb +3 -1
- data/lib/ki/middleware/base_middleware.rb +2 -0
- data/lib/ki/middleware/coffee_compiler.rb +2 -0
- data/lib/ki/middleware/haml_compiler.rb +2 -0
- data/lib/ki/middleware/helpers/format_of_helper.rb +3 -1
- data/lib/ki/middleware/helpers/haml_compiler_helper.rb +7 -5
- data/lib/ki/middleware/helpers/public_file_helper.rb +2 -0
- data/lib/ki/middleware/helpers/redirect_to_helper.rb +2 -0
- data/lib/ki/middleware/helpers/view_helper.rb +2 -0
- data/lib/ki/middleware/init_middleware.rb +2 -0
- data/lib/ki/middleware/{doc_generator.rb → insta_doc.rb} +8 -6
- data/lib/ki/middleware/public_file_server.rb +2 -0
- data/lib/ki/middleware/realtime.rb +3 -1
- data/lib/ki/middleware/sass_compiler.rb +2 -0
- data/lib/ki/model.rb +8 -7
- data/lib/ki/modules/callbacks.rb +12 -20
- data/lib/ki/modules/model_helper.rb +2 -0
- data/lib/ki/modules/query_interface.rb +2 -0
- data/lib/ki/modules/restrictions.rb +5 -2
- data/lib/ki/orm.rb +20 -13
- data/lib/ki/utils/annotations.rb +33 -0
- data/lib/ki/utils/api_error.rb +14 -4
- data/lib/ki/utils/descendants.rb +9 -0
- data/lib/ki/utils/extra_irb.rb +3 -1
- data/lib/ki/utils/extra_ruby.rb +12 -0
- data/lib/ki/utils/indifferent_hash.rb +2 -0
- data/lib/ki/utils/logger.rb +5 -0
- data/lib/ki/version.rb +3 -1
- data/lib/ki/views/instadoc.haml +49 -9
- data/spec/config.yml.example +1 -1
- data/spec/examples/base/client.rb +20 -0
- data/spec/examples/base/logs/.keep +0 -0
- data/spec/examples/base/public/javascripts/app.js +12 -0
- data/spec/examples/json.northpole.ro/.ruby-version +1 -1
- data/spec/examples/json.northpole.ro/public/javascripts/app.coffee +1 -0
- data/spec/functional_spec.rb +2 -0
- data/spec/lib/ki/base_request_spec.rb +25 -23
- data/spec/lib/ki/channel_manager_spec.rb +2 -0
- data/spec/lib/ki/helpers_spec.rb +4 -2
- data/spec/lib/ki/indifferent_hash_spec.rb +2 -0
- data/spec/lib/ki/ki_app_spec.rb +2 -0
- data/spec/lib/ki/ki_config_spec.rb +5 -3
- data/spec/lib/ki/ki_spec.rb +2 -0
- data/spec/lib/ki/middleware/admin_generator_spec.rb +2 -0
- data/spec/lib/ki/middleware/haml_compiler_spec.rb +6 -3
- data/spec/lib/ki/middleware/helpers/format_of_helper_spec.rb +4 -2
- data/spec/lib/ki/middleware/init_middleware_spec.rb +3 -1
- data/spec/lib/ki/middleware/{doc_generator_spec.rb → insta_doc_spec.rb} +3 -1
- data/spec/lib/ki/middleware/realtime_spec.rb +12 -6
- data/spec/lib/ki/middleware_spec.rb +3 -1
- data/spec/lib/ki/model_spec.rb +26 -22
- data/spec/lib/ki/modules/model_helper_spec.rb +5 -3
- data/spec/lib/ki/modules/restrictions_spec.rb +16 -0
- data/spec/lib/ki/orm_spec.rb +18 -15
- data/spec/lib/ki/utils/api_error_spec.rb +9 -0
- data/spec/spec_helper.rb +13 -7
- data/spec/util_spec.rb +3 -1
- metadata +44 -30
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ki
|
2
4
|
class Model
|
3
5
|
module Restrictions
|
@@ -28,9 +30,10 @@ module Ki
|
|
28
30
|
private
|
29
31
|
|
30
32
|
def generic_restriction(method_name, attributes)
|
31
|
-
|
33
|
+
attributes += send(method_name) if defined? method_name
|
34
|
+
%i[define_method define_singleton_method].each do |definition_means|
|
32
35
|
send definition_means, method_name do
|
33
|
-
attributes
|
36
|
+
attributes.sort.uniq
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
data/lib/ki/orm.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'singleton'
|
2
4
|
|
3
5
|
module Ki
|
@@ -29,14 +31,16 @@ module Ki
|
|
29
31
|
# config.yml.
|
30
32
|
#
|
31
33
|
def establish_connection
|
34
|
+
Mongo::Logger.logger.level = Logger::FATAL
|
35
|
+
# Mongo::Logger.logger = Logger.new('mongo.log')
|
36
|
+
# Mongo::Logger.logger.level = Logger::INFO
|
37
|
+
|
32
38
|
@config = KiConfig.instance.database
|
33
|
-
if ENV['MONGODB_URI']
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@db = @connection.db(@config['name'])
|
39
|
-
end
|
39
|
+
@db = if ENV['MONGODB_URI']
|
40
|
+
Mongo::Client.new
|
41
|
+
else
|
42
|
+
Mongo::Client.new('mongodb://' + connection_string)
|
43
|
+
end
|
40
44
|
self
|
41
45
|
end
|
42
46
|
|
@@ -76,8 +80,9 @@ module Ki
|
|
76
80
|
# db.insert 'users', { name: 'Homer' }
|
77
81
|
#
|
78
82
|
def insert(name, hash)
|
79
|
-
@db[name].
|
80
|
-
[
|
83
|
+
item = @db[name].insert_one(hash)
|
84
|
+
hash['id'] = item.inserted_id.to_s
|
85
|
+
hash
|
81
86
|
end
|
82
87
|
|
83
88
|
# Find a hash in the database
|
@@ -137,7 +142,7 @@ module Ki
|
|
137
142
|
hash = nourish_hash_id hash
|
138
143
|
id = hash['_id'].to_s
|
139
144
|
hash.delete('_id')
|
140
|
-
@db[name].
|
145
|
+
@db[name].update_one({ '_id' => BSON::ObjectId(id) }, hash)
|
141
146
|
hash['id'] = id
|
142
147
|
hash
|
143
148
|
end
|
@@ -161,9 +166,9 @@ module Ki
|
|
161
166
|
#
|
162
167
|
def delete(name, hash)
|
163
168
|
hash = nourish_hash_id hash
|
164
|
-
r = @db[name].
|
169
|
+
r = @db[name].delete_many hash
|
165
170
|
{
|
166
|
-
deleted_item_count: r['n']
|
171
|
+
deleted_item_count: r.documents[0]['n']
|
167
172
|
}
|
168
173
|
end
|
169
174
|
|
@@ -206,6 +211,7 @@ module Ki
|
|
206
211
|
hash
|
207
212
|
end
|
208
213
|
|
214
|
+
# TODO: write tests
|
209
215
|
def nourish_hash_limit(hash)
|
210
216
|
tmp = {}
|
211
217
|
if hash['__limit']
|
@@ -217,6 +223,7 @@ module Ki
|
|
217
223
|
[hash, tmp]
|
218
224
|
end
|
219
225
|
|
226
|
+
# TODO: write tests
|
220
227
|
def nourish_hash_sort(hash)
|
221
228
|
tmp = {}
|
222
229
|
if hash['__sort']
|
@@ -227,7 +234,7 @@ module Ki
|
|
227
234
|
# TODO: validate value
|
228
235
|
# TODO: handle sorting by id
|
229
236
|
hash['__sort'].to_a.each do |e|
|
230
|
-
tmp[e[0].to_sym] = e[1]
|
237
|
+
tmp[e[0].to_sym] = e[1]
|
231
238
|
end
|
232
239
|
end
|
233
240
|
hash.delete('__sort')
|
@@ -0,0 +1,33 @@
|
|
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
|
data/lib/ki/utils/api_error.rb
CHANGED
@@ -1,14 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Ki
|
2
4
|
class ApiError < StandardError #:nodoc:
|
5
|
+
extend Descendants
|
6
|
+
|
3
7
|
attr_reader :status
|
4
8
|
|
5
|
-
def initialize(body, status = 400)
|
6
|
-
super body
|
9
|
+
def initialize(body = nil, status = 400)
|
10
|
+
super body.nil? ? to_s : body
|
7
11
|
@status = status
|
8
12
|
end
|
9
13
|
|
10
14
|
def result
|
11
|
-
{
|
15
|
+
{
|
16
|
+
'error' => to_s,
|
17
|
+
'status' => @status
|
18
|
+
}
|
12
19
|
end
|
13
20
|
end
|
14
21
|
|
@@ -34,8 +41,11 @@ module Ki
|
|
34
41
|
end
|
35
42
|
|
36
43
|
class PartialNotFoundError < ApiError #:nodoc:
|
37
|
-
def initialize(s)
|
44
|
+
def initialize(s = '"partial"')
|
38
45
|
super "partial #{s} not found", 404
|
39
46
|
end
|
40
47
|
end
|
41
48
|
end
|
49
|
+
|
50
|
+
class CustomError < Ki::ApiError
|
51
|
+
end
|
data/lib/ki/utils/extra_irb.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# http://jasonroelofs.com/2009/04/02/embedding-irb-into-your-ruby-application/
|
2
4
|
require 'irb'
|
3
5
|
|
@@ -15,7 +17,7 @@ module IRB # :nodoc:
|
|
15
17
|
|
16
18
|
irb = Irb.new(workspace)
|
17
19
|
|
18
|
-
@CONF[:IRB_RC]
|
20
|
+
@CONF[:IRB_RC]&.call(irb.context)
|
19
21
|
@CONF[:MAIN_CONTEXT] = irb.context
|
20
22
|
|
21
23
|
catch(:IRB_EXIT) do
|
data/lib/ki/utils/extra_ruby.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class String
|
2
4
|
# Converts a string to a class
|
3
5
|
#
|
@@ -45,3 +47,13 @@ class NilClass
|
|
45
47
|
false
|
46
48
|
end
|
47
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
|
data/lib/ki/version.rb
CHANGED
data/lib/ki/views/instadoc.haml
CHANGED
@@ -5,12 +5,52 @@
|
|
5
5
|
%li
|
6
6
|
%div
|
7
7
|
%h2= model.to_s
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
%
|
12
|
-
|
13
|
-
= model.
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
data/spec/config.yml.example
CHANGED
@@ -0,0 +1,20 @@
|
|
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
|
+
}
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
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');}
|
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.3
|
data/spec/functional_spec.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Ki::BaseRequest do
|
4
6
|
let(:req) { Ki::BaseRequest }
|
5
7
|
|
6
8
|
it 'knows if current path is root' do
|
7
|
-
req.new({ 'PATH_INFO' => '/' }).
|
8
|
-
req.new({ 'PATH_INFO' => '/foo' }).
|
9
|
+
expect(req.new({ 'PATH_INFO' => '/' })).to be_root
|
10
|
+
expect(req.new({ 'PATH_INFO' => '/foo' })).to_not be_root
|
9
11
|
end
|
10
12
|
|
11
13
|
it 'coverts path to class' do
|
@@ -13,37 +15,37 @@ describe Ki::BaseRequest do
|
|
13
15
|
class Bar < Ki::Model; end
|
14
16
|
class FooBar < Ki::Model; end
|
15
17
|
|
16
|
-
req.new({ 'PATH_INFO' => '' }).to_ki_model_class.
|
17
|
-
req.new({ 'PATH_INFO' => '/' }).to_ki_model_class.
|
18
|
-
req.new({ 'PATH_INFO' => 'foo' }).to_ki_model_class.
|
19
|
-
req.new({ 'PATH_INFO' => '/foo' }).to_ki_model_class.
|
20
|
-
req.new({ 'PATH_INFO' => '/Foo.json' }).to_ki_model_class.
|
21
|
-
req.new({ 'PATH_INFO' => '/bar.json' }).to_ki_model_class.
|
22
|
-
req.new({ 'PATH_INFO' => '/Foo/bar.json' }).to_ki_model_class.
|
23
|
-
req.new({ 'PATH_INFO' => '/Foo/bar' }).to_ki_model_class.
|
24
|
-
req.new({ 'PATH_INFO' => '/Foo_bar' }).to_ki_model_class.
|
25
|
-
req.new({ 'PATH_INFO' => '/foo_bar' }).to_ki_model_class.
|
26
|
-
req.new({ 'PATH_INFO' => '/foo_bar.json' }).to_ki_model_class.
|
18
|
+
expect(req.new({ 'PATH_INFO' => '' }).to_ki_model_class).to be nil
|
19
|
+
expect(req.new({ 'PATH_INFO' => '/' }).to_ki_model_class).to be nil
|
20
|
+
expect(req.new({ 'PATH_INFO' => 'foo' }).to_ki_model_class).to be Foo
|
21
|
+
expect(req.new({ 'PATH_INFO' => '/foo' }).to_ki_model_class).to be Foo
|
22
|
+
expect(req.new({ 'PATH_INFO' => '/Foo.json' }).to_ki_model_class).to be Foo
|
23
|
+
expect(req.new({ 'PATH_INFO' => '/bar.json' }).to_ki_model_class).to be Bar
|
24
|
+
expect(req.new({ 'PATH_INFO' => '/Foo/bar.json' }).to_ki_model_class).to be nil
|
25
|
+
expect(req.new({ 'PATH_INFO' => '/Foo/bar' }).to_ki_model_class).to be nil
|
26
|
+
expect(req.new({ 'PATH_INFO' => '/Foo_bar' }).to_ki_model_class).to be FooBar
|
27
|
+
expect(req.new({ 'PATH_INFO' => '/foo_bar' }).to_ki_model_class).to be FooBar
|
28
|
+
expect(req.new({ 'PATH_INFO' => '/foo_bar.json' }).to_ki_model_class).to be FooBar
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'converts verb to action' do
|
30
|
-
req.new({ 'REQUEST_METHOD' => 'GET' }).to_action.
|
31
|
-
req.new({ 'REQUEST_METHOD' => 'POST' }).to_action.
|
32
|
-
req.new({ 'REQUEST_METHOD' => 'PUT' }).to_action.
|
33
|
-
req.new({ 'REQUEST_METHOD' => 'DELETE' }).to_action.
|
34
|
-
req.new({ 'REQUEST_METHOD' => 'SEARCH' }).to_action.
|
32
|
+
expect(req.new({ 'REQUEST_METHOD' => 'GET' }).to_action).to be :find
|
33
|
+
expect(req.new({ 'REQUEST_METHOD' => 'POST' }).to_action).to be :create
|
34
|
+
expect(req.new({ 'REQUEST_METHOD' => 'PUT' }).to_action).to be :update
|
35
|
+
expect(req.new({ 'REQUEST_METHOD' => 'DELETE' }).to_action).to be :delete
|
36
|
+
expect(req.new({ 'REQUEST_METHOD' => 'SEARCH' }).to_action).to be :find
|
35
37
|
end
|
36
38
|
|
37
39
|
context 'json' do
|
38
40
|
it 'considers application/json content type as a json request' do
|
39
|
-
req.new({ 'CONTENT_TYPE' => 'application/xml' }).
|
40
|
-
req.new({}).
|
41
|
-
req.new({ 'CONTENT_TYPE' => 'application/json' }).
|
41
|
+
expect(req.new({ 'CONTENT_TYPE' => 'application/xml' })).to_not be_json
|
42
|
+
expect(req.new({})).to_not be_json
|
43
|
+
expect(req.new({ 'CONTENT_TYPE' => 'application/json' })).to be_json
|
42
44
|
end
|
43
45
|
|
44
46
|
it 'considers .json url format as a json request' do
|
45
|
-
req.new({ 'PATH_INFO' => '/foo' }).
|
46
|
-
req.new({ 'PATH_INFO' => '/foo.json' }).
|
47
|
+
expect(req.new({ 'PATH_INFO' => '/foo' })).to_not be_json
|
48
|
+
expect(req.new({ 'PATH_INFO' => '/foo.json' })).to be_json
|
47
49
|
end
|
48
50
|
|
49
51
|
# context 'params' do
|