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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +9 -0
  4. data/.ruby-gemset +1 -1
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +10 -2
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +95 -85
  9. data/Guardfile +20 -14
  10. data/MIDDLEWARE.md +2 -2
  11. data/README.md +29 -1
  12. data/Rakefile +3 -1
  13. data/bin/ki +1 -0
  14. data/ki.gemspec +15 -14
  15. data/lib/ki.rb +8 -1
  16. data/lib/ki/base_request.rb +7 -5
  17. data/lib/ki/channel_manager.rb +2 -0
  18. data/lib/ki/helpers.rb +4 -5
  19. data/lib/ki/ki.rb +11 -2
  20. data/lib/ki/ki_app.rb +2 -0
  21. data/lib/ki/ki_cli.rb +9 -2
  22. data/lib/ki/ki_config.rb +5 -2
  23. data/lib/ki/ki_logger.rb +15 -0
  24. data/lib/ki/middleware/admin_interface_generator.rb +3 -1
  25. data/lib/ki/middleware/api_handler.rb +3 -1
  26. data/lib/ki/middleware/base_middleware.rb +2 -0
  27. data/lib/ki/middleware/coffee_compiler.rb +2 -0
  28. data/lib/ki/middleware/haml_compiler.rb +2 -0
  29. data/lib/ki/middleware/helpers/format_of_helper.rb +3 -1
  30. data/lib/ki/middleware/helpers/haml_compiler_helper.rb +7 -5
  31. data/lib/ki/middleware/helpers/public_file_helper.rb +2 -0
  32. data/lib/ki/middleware/helpers/redirect_to_helper.rb +2 -0
  33. data/lib/ki/middleware/helpers/view_helper.rb +2 -0
  34. data/lib/ki/middleware/init_middleware.rb +2 -0
  35. data/lib/ki/middleware/{doc_generator.rb → insta_doc.rb} +8 -6
  36. data/lib/ki/middleware/public_file_server.rb +2 -0
  37. data/lib/ki/middleware/realtime.rb +3 -1
  38. data/lib/ki/middleware/sass_compiler.rb +2 -0
  39. data/lib/ki/model.rb +8 -7
  40. data/lib/ki/modules/callbacks.rb +12 -20
  41. data/lib/ki/modules/model_helper.rb +2 -0
  42. data/lib/ki/modules/query_interface.rb +2 -0
  43. data/lib/ki/modules/restrictions.rb +5 -2
  44. data/lib/ki/orm.rb +20 -13
  45. data/lib/ki/utils/annotations.rb +33 -0
  46. data/lib/ki/utils/api_error.rb +14 -4
  47. data/lib/ki/utils/descendants.rb +9 -0
  48. data/lib/ki/utils/extra_irb.rb +3 -1
  49. data/lib/ki/utils/extra_ruby.rb +12 -0
  50. data/lib/ki/utils/indifferent_hash.rb +2 -0
  51. data/lib/ki/utils/logger.rb +5 -0
  52. data/lib/ki/version.rb +3 -1
  53. data/lib/ki/views/instadoc.haml +49 -9
  54. data/spec/config.yml.example +1 -1
  55. data/spec/examples/base/client.rb +20 -0
  56. data/spec/examples/base/logs/.keep +0 -0
  57. data/spec/examples/base/public/javascripts/app.js +12 -0
  58. data/spec/examples/json.northpole.ro/.ruby-version +1 -1
  59. data/spec/examples/json.northpole.ro/public/javascripts/app.coffee +1 -0
  60. data/spec/functional_spec.rb +2 -0
  61. data/spec/lib/ki/base_request_spec.rb +25 -23
  62. data/spec/lib/ki/channel_manager_spec.rb +2 -0
  63. data/spec/lib/ki/helpers_spec.rb +4 -2
  64. data/spec/lib/ki/indifferent_hash_spec.rb +2 -0
  65. data/spec/lib/ki/ki_app_spec.rb +2 -0
  66. data/spec/lib/ki/ki_config_spec.rb +5 -3
  67. data/spec/lib/ki/ki_spec.rb +2 -0
  68. data/spec/lib/ki/middleware/admin_generator_spec.rb +2 -0
  69. data/spec/lib/ki/middleware/haml_compiler_spec.rb +6 -3
  70. data/spec/lib/ki/middleware/helpers/format_of_helper_spec.rb +4 -2
  71. data/spec/lib/ki/middleware/init_middleware_spec.rb +3 -1
  72. data/spec/lib/ki/middleware/{doc_generator_spec.rb → insta_doc_spec.rb} +3 -1
  73. data/spec/lib/ki/middleware/realtime_spec.rb +12 -6
  74. data/spec/lib/ki/middleware_spec.rb +3 -1
  75. data/spec/lib/ki/model_spec.rb +26 -22
  76. data/spec/lib/ki/modules/model_helper_spec.rb +5 -3
  77. data/spec/lib/ki/modules/restrictions_spec.rb +16 -0
  78. data/spec/lib/ki/orm_spec.rb +18 -15
  79. data/spec/lib/ki/utils/api_error_spec.rb +9 -0
  80. data/spec/spec_helper.rb +13 -7
  81. data/spec/util_spec.rb +3 -1
  82. metadata +44 -30
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ki
2
4
  class Model
3
5
  module ModelHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ki
2
4
  class Model
3
5
  # the query interface does not respect before/after filters,
@@ -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
- [:define_method, :define_singleton_method].each do |definition_means|
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
@@ -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
- @connection = Mongo::Connection.new
35
- @db = @connection.db
36
- else
37
- @connection = Mongo::Connection.new(@config['host'], @config['port'])
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].insert(hash)
80
- [hash].stringify_ids.first
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].update({ '_id' => BSON::ObjectId(id) }, hash)
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].remove hash
169
+ r = @db[name].delete_many hash
165
170
  {
166
- deleted_item_count: r['n'] || 0
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].to_sym
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
@@ -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
- { 'error' => to_s }
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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ki
4
+ module Descendants
5
+ def descendants
6
+ ObjectSpace.each_object(Class).select { |klass| klass < self }
7
+ end
8
+ end
9
+ end
@@ -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].call(irb.context) if @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
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class IndifferentHash < Hash
2
4
  def []=(key, val)
3
5
  key = key.to_sym
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ::Logger
4
+ alias write <<
5
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Ki
2
- VERSION = '0.4.11'
4
+ VERSION = '0.4.12'
3
5
  end
@@ -5,12 +5,52 @@
5
5
  %li
6
6
  %div
7
7
  %h2= model.to_s
8
- %p
9
- Required attributes:
10
- = model.required_attributes
11
- %p
12
- Forbidden actions:
13
- = model.forbidden_actions
14
- %p
15
- Unique attributes:
16
- = model.unique_attributes
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
@@ -7,7 +7,7 @@ development:
7
7
  test:
8
8
  middleware: [
9
9
  'ApiHandler',
10
- 'DocGenerator',
10
+ 'InstaDoc',
11
11
  'AdminInterfaceGenerator',
12
12
  'CoffeeCompiler',
13
13
  'SassCompiler',
@@ -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
1
+ 2.4.3
@@ -36,6 +36,7 @@ window.syntaxHighlight = (json) ->
36
36
  "<span class=\"" + cls + "\">" + match + "</span>"
37
37
 
38
38
  $(document).ready ->
39
+ jNorthPole.BASE_URL = 'http://localhost:1337/'
39
40
  setActiveMenuClass()
40
41
  setInterval(randomlyFlipBear, 1000)
41
42
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe 'the app' do
@@ -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' => '/' }).root?.should be_true
8
- req.new({ 'PATH_INFO' => '/foo' }).root?.should be_false
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.should be nil
17
- req.new({ 'PATH_INFO' => '/' }).to_ki_model_class.should be nil
18
- req.new({ 'PATH_INFO' => 'foo' }).to_ki_model_class.should be Foo
19
- req.new({ 'PATH_INFO' => '/foo' }).to_ki_model_class.should be Foo
20
- req.new({ 'PATH_INFO' => '/Foo.json' }).to_ki_model_class.should be Foo
21
- req.new({ 'PATH_INFO' => '/bar.json' }).to_ki_model_class.should be Bar
22
- req.new({ 'PATH_INFO' => '/Foo/bar.json' }).to_ki_model_class.should be nil
23
- req.new({ 'PATH_INFO' => '/Foo/bar' }).to_ki_model_class.should be nil
24
- req.new({ 'PATH_INFO' => '/Foo_bar' }).to_ki_model_class.should be FooBar
25
- req.new({ 'PATH_INFO' => '/foo_bar' }).to_ki_model_class.should be FooBar
26
- req.new({ 'PATH_INFO' => '/foo_bar.json' }).to_ki_model_class.should be FooBar
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.should be :find
31
- req.new({ 'REQUEST_METHOD' => 'POST' }).to_action.should be :create
32
- req.new({ 'REQUEST_METHOD' => 'PUT' }).to_action.should be :update
33
- req.new({ 'REQUEST_METHOD' => 'DELETE' }).to_action.should be :delete
34
- req.new({ 'REQUEST_METHOD' => 'SEARCH' }).to_action.should be :find
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' }).json?.should be_false
40
- req.new({}).json?.should be_false
41
- req.new({ 'CONTENT_TYPE' => 'application/json' }).json?.should be_true
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' }).json?.should be_false
46
- req.new({ 'PATH_INFO' => '/foo.json' }).json?.should be_true
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