ki 0.4.11 → 0.4.12

Sign up to get free protection for your applications and to get access to all the features.
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