ditty 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5c1aeccd30ff2045fdc743913e43590e7cc5ca03
4
- data.tar.gz: 5c85c18dfd395210a4c64a11b9d801d8afaf2360
3
+ metadata.gz: 2b443ee45c38fca3c0f69379d0631b03f1f91263
4
+ data.tar.gz: 3b26f6324b6979e1e517c6338e41cb994dce80c0
5
5
  SHA512:
6
- metadata.gz: 9152764039916756b7c355de8466597f83f64921122e969f5dec13a5376c3310ab4d2c5e95576abf583bc86aa6a0ad0e1a60a7c3fdb0a8cde3b5946523d3fd22
7
- data.tar.gz: c1df6170f906e47a460abb59a52ee4054ae5434daa926c6da8062cecda9e1554227efdfb936fd5204a848436cb08af55d1a7bcffa5b8f05870c8fba7fb57513d
6
+ metadata.gz: 6794d0ac24c5fa6f0e95bd98e90cf8456f42b12c3cc8b3c0f3933f76a01912e1f62a798eddbc0076068351077b08d9e80e1071305630a256a9f27a78091071c3
7
+ data.tar.gz: 4356aa0fca5ffd090d90a39450d2d8f5dfa90286aa9d0c66e7708ded6e1669d1cfa853ea5d4dbab9dc34476c233e0f2e72b5bed759833b82f55cd7b52de561b3
data/.travis.yml CHANGED
@@ -11,6 +11,9 @@ before_install:
11
11
  - gem install bundler -v 1.12.5
12
12
  before_script:
13
13
  - bundle exec rake ditty:prep
14
+ script:
15
+ - bundle exec rake
16
+ - bundle exec rubocop --fail-level W lib views
14
17
  addons:
15
18
  code_climate:
16
19
  repo_token: 289860573c6284a8e277de86848caba84d840be49e35f3601bcd672ab40d1e35
@@ -50,22 +50,62 @@ module Ditty
50
50
  end
51
51
 
52
52
  not_found do
53
- haml :'404', locals: { title: '4 oh 4' }
53
+ respond_to do |format|
54
+ format.html do
55
+ haml :'404', locals: { title: '4 oh 4' }
56
+ end
57
+ format.json do
58
+ [404, { 'Content-Type' => 'application/json' }, [{ code: 404, errors: ['Not Found'] }.to_json]]
59
+ end
60
+ end
54
61
  end
55
62
 
56
63
  error do
57
64
  error = env['sinatra.error']
58
- haml :error, locals: { title: 'Something went wrong', error: error }
65
+ broadcast(:application_error, error)
66
+ respond_to do |format|
67
+ format.html do
68
+ haml :error, locals: { title: 'Something went wrong', error: error }
69
+ end
70
+ format.json do
71
+ [500, { 'Content-Type' => 'application/json' }, [{ code: 500, errors: ['Something went wrong'] }.to_json]]
72
+ end
73
+ end
59
74
  end
60
75
 
61
76
  error Helpers::NotAuthenticated do
62
- flash[:warning] = 'Please log in first.'
63
- redirect "#{settings.map_path}/auth/identity"
77
+ respond_to do |format|
78
+ format.html do
79
+ flash[:warning] = 'Please log in first.'
80
+ redirect "#{settings.map_path}/auth/identity"
81
+ end
82
+ format.json do
83
+ [401, { 'Content-Type' => 'application/json' }, [{ code: 401, errors: ['Not Authenticated'] }.to_json]]
84
+ end
85
+ end
64
86
  end
65
87
 
66
88
  error ::Pundit::NotAuthorizedError do
67
- flash[:warning] = 'Please log in first.'
68
- redirect "#{settings.map_path}/auth/identity"
89
+ respond_to do |format|
90
+ format.html do
91
+ flash[:warning] = 'Please log in first.'
92
+ redirect "#{settings.map_path}/auth/identity"
93
+ end
94
+ format.json do
95
+ [401, { 'Content-Type' => 'application/json' }, [{ code: 401, errors: ['Not Authorized'] }.to_json]]
96
+ end
97
+ end
98
+ end
99
+
100
+ error ::Sequel::ForeignKeyConstraintViolation do
101
+ respond_to do |format|
102
+ format.html do
103
+ haml :error, locals: { title: 'Something went wrong', error: error }
104
+ end
105
+ format.json do
106
+ [400, { 'Content-Type' => 'application/json' }, [{ code: 400, errors: ['Invalid Relation Specified'] }.to_json]]
107
+ end
108
+ end
69
109
  end
70
110
 
71
111
  before(/.*/) do
@@ -13,6 +13,10 @@ module Ditty
13
13
  set view_location: nil
14
14
  set track_actions: false
15
15
 
16
+ def read(id)
17
+ dataset.first(settings.model_class.primary_key => id)
18
+ end
19
+
16
20
  # List
17
21
  get '/', provides: %i[html json] do
18
22
  authorize settings.model_class, :list
@@ -29,8 +33,8 @@ module Ditty
29
33
  format.json do
30
34
  # TODO: Add links defined by actions (New #{heading})
31
35
  json(
32
- 'items' => list.map(&:for_json),
33
- 'page' => params[:page],
36
+ 'items' => list.all.map(&:for_json),
37
+ 'page' => (params['page'] || 1).to_i,
34
38
  'count' => list.count,
35
39
  'total' => dataset.count
36
40
  )
@@ -63,11 +67,11 @@ module Ditty
63
67
  end
64
68
  end
65
69
  format.json do
66
- headers 'Content-Type' => 'application/json'
70
+ content_type :json
67
71
  if success
68
72
  redirect "#{base_path}/#{entity.id}", 201
69
73
  else
70
- 400
74
+ [400, { errors: entity.errors }.to_json]
71
75
  end
72
76
  end
73
77
  end
@@ -75,7 +79,7 @@ module Ditty
75
79
 
76
80
  # Read
77
81
  get '/:id' do |id|
78
- entity = dataset.first(settings.model_class.primary_key => id)
82
+ entity = read(id)
79
83
  halt 404 unless entity
80
84
  authorize entity, :read
81
85
 
@@ -97,7 +101,7 @@ module Ditty
97
101
 
98
102
  # Update Form
99
103
  get '/:id/edit' do |id|
100
- entity = dataset.first(settings.model_class.primary_key => id)
104
+ entity = read(id)
101
105
  halt 404 unless entity
102
106
  authorize entity, :update
103
107
 
@@ -106,7 +110,7 @@ module Ditty
106
110
 
107
111
  # Update
108
112
  put '/:id' do |id|
109
- entity = dataset.first(settings.model_class.primary_key => id)
113
+ entity = read(id)
110
114
  halt 404 unless entity
111
115
  authorize entity, :update
112
116
 
@@ -117,6 +121,7 @@ module Ditty
117
121
  if success
118
122
  respond_to do |format|
119
123
  format.html do
124
+ # TODO: Ability to customize the return path and message?
120
125
  flash[:success] = "#{heading} Updated"
121
126
  redirect "#{base_path}/#{entity.id}"
122
127
  end
@@ -131,14 +136,15 @@ module Ditty
131
136
  haml :"#{view_location}/edit", locals: { entity: entity, title: heading(:edit) }
132
137
  end
133
138
  format.json do
134
- 400
139
+ content_type :json
140
+ [400, { errors: entity.errors }.to_json]
135
141
  end
136
142
  end
137
143
  end
138
144
  end
139
145
 
140
146
  delete '/:id' do |id|
141
- entity = dataset.first(settings.model_class.primary_key => id)
147
+ entity = read(id)
142
148
  halt 404 unless entity
143
149
  authorize entity, :delete
144
150
 
@@ -151,7 +157,7 @@ module Ditty
151
157
  redirect base_path.to_s
152
158
  end
153
159
  format.json do
154
- content_type 'application/json'
160
+ content_type :json
155
161
  headers 'Location' => '/users'
156
162
  status 204
157
163
  end
@@ -59,11 +59,11 @@ module Ditty
59
59
  respond_to do |format|
60
60
  format.html do
61
61
  flash[:success] = 'User created'
62
- redirect "/users/#{user.id}"
62
+ redirect "#{base_path}/#{user.id}"
63
63
  end
64
64
  format.json do
65
65
  headers 'Content-Type' => 'application/json'
66
- redirect "/users/#{user.id}", 201
66
+ redirect "#{base_path}/#{user.id}", 201
67
67
  end
68
68
  end
69
69
  else
@@ -87,7 +87,7 @@ module Ditty
87
87
 
88
88
  # Update
89
89
  put '/:id' do |id|
90
- entity = dataset[id.to_i]
90
+ entity = dataset.first(settings.model_class.primary_key => id)
91
91
  halt 404 unless entity
92
92
  authorize entity, :update
93
93
 
@@ -105,7 +105,7 @@ module Ditty
105
105
  respond_to do |format|
106
106
  format.html do
107
107
  flash[:success] = "#{heading} Updated"
108
- redirect "/users/#{entity.id}"
108
+ redirect back
109
109
  end
110
110
  format.json do
111
111
  content_type 'application/json'
@@ -127,7 +127,7 @@ module Ditty
127
127
  end
128
128
 
129
129
  put '/:id/identity' do |id|
130
- entity = dataset[id.to_i]
130
+ entity = dataset.first(settings.model_class.primary_key => id)
131
131
  halt 404 unless entity
132
132
  authorize entity, :update
133
133
 
@@ -151,7 +151,7 @@ module Ditty
151
151
  log_action("#{dehumanized}_update_password".to_sym) if settings.track_actions
152
152
  flash[:success] = 'Password Updated'
153
153
  redirect back
154
- elsif current_user.super_admin? && current_user.id != id
154
+ elsif current_user.super_admin? && current_user.id != id.to_i
155
155
  haml :"#{view_location}/display", locals: { entity: entity, identity: identity, title: heading }
156
156
  else
157
157
  haml :"#{view_location}/profile", locals: { entity: entity, identity: identity, title: heading }
@@ -160,7 +160,7 @@ module Ditty
160
160
 
161
161
  # Delete
162
162
  delete '/:id', provides: %i[html json] do |id|
163
- entity = dataset[id.to_i]
163
+ entity = dataset.first(settings.model_class.primary_key => id)
164
164
  halt 404 unless entity
165
165
  authorize entity, :delete
166
166
 
@@ -172,7 +172,7 @@ module Ditty
172
172
  respond_to do |format|
173
173
  format.html do
174
174
  flash[:success] = "#{heading} Deleted"
175
- redirect '/users'
175
+ redirect base_path
176
176
  end
177
177
  format.json do
178
178
  content_type 'application/json'
@@ -4,11 +4,10 @@ module Ditty
4
4
  module Helpers
5
5
  module Authentication
6
6
  def current_user
7
- if env['rack.session'].nil? || env['rack.session']['user_id'].nil?
8
- self.current_user = anonymous_user
9
- end
7
+ user_id = current_user_id
8
+ self.current_user = anonymous_user if user_id.nil?
10
9
  @users ||= Hash.new { |h, k| h[k] = User[k] }
11
- @users[env['rack.session']['user_id']]
10
+ @users[user_id]
12
11
  end
13
12
 
14
13
  def current_user=(user)
@@ -16,6 +15,11 @@ module Ditty
16
15
  env['rack.session']['user_id'] = user.id if user
17
16
  end
18
17
 
18
+ def current_user_id
19
+ return env['omniauth.auth'].uid if env['omniauth.auth']
20
+ env['rack.session']['user_id'] if env['rack.session']
21
+ end
22
+
19
23
  def authenticate
20
24
  authenticated?
21
25
  end
@@ -13,10 +13,10 @@ module Ditty
13
13
  end
14
14
 
15
15
  def list
16
- params['count'] ||= 10
17
- params['page'] ||= 1
16
+ count = params['count'] || 10
17
+ page = params['page'] || 1
18
18
 
19
- dataset.select.paginate(params['page'].to_i, params['count'].to_i)
19
+ dataset.select.paginate(page.to_i, count.to_i)
20
20
  end
21
21
 
22
22
  def heading(action = nil)
@@ -46,10 +46,14 @@ module Ditty
46
46
  haml :'partials/delete_form', locals: locals
47
47
  end
48
48
 
49
- def pagination(list, base_path)
49
+ def pagination(list, base_path, qp = {})
50
+ qs = params.clone.merge(qp)
51
+ qs.delete('captures')
50
52
  locals = {
51
- next_link: list.last_page? ? '#' : "#{base_path}?page=#{list.next_page}&count=#{list.page_size}",
52
- prev_link: list.first_page? ? '#' : "#{base_path}?page=#{list.prev_page}&count=#{list.page_size}",
53
+ first_link: "#{base_path}?#{Rack::Utils.build_query(qs.merge('page' => 1))}",
54
+ next_link: list.last_page? ? '#' : "#{base_path}?#{Rack::Utils.build_query(qs.merge('page' => list.next_page))}",
55
+ prev_link: list.first_page? ? '#' : "#{base_path}?#{Rack::Utils.build_query(qs.merge('page' => list.prev_page))}",
56
+ last_link: "#{base_path}?#{Rack::Utils.build_query(qs.merge('page' => list.page_count))}",
53
57
  base_path: base_path,
54
58
  list: list
55
59
  }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'wisper'
2
4
 
3
5
  module Ditty
@@ -11,7 +13,7 @@ module Ditty
11
13
  return unless args[0].is_a? Hash
12
14
  vals[:user] = args[0][:user] if args[0] && args[0].key?(:user)
13
15
  vals[:details] = args[0][:details] if args[0] && args[0].key?(:details)
14
- @mutex.synchronize { AuditLog.create vals }
16
+ @mutex.synchronize { Ditty::AuditLog.create vals }
15
17
  end
16
18
 
17
19
  def respond_to_missing?(_method, _include_private = false)
@@ -20,4 +22,4 @@ module Ditty
20
22
  end
21
23
  end
22
24
 
23
- Wisper.subscribe(Ditty::Listener.new)
25
+ Wisper.subscribe(Ditty::Listener.new) unless ENV['RACK_ENV'] == 'test'
@@ -0,0 +1,20 @@
1
+ module Ditty
2
+ module Middleware
3
+ class ErrorCatchall
4
+ attr_reader :env
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ @env = env
12
+ begin
13
+ @app.call env
14
+ rescue StandardError
15
+ [500, {}, ['Unknown Error']]
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -32,13 +32,13 @@ module Ditty
32
32
  puts 'Preparing the Ditty public folder'
33
33
  Dir.mkdir 'public' unless File.exist?('public')
34
34
  ::Ditty::Components.public_folder.each do |path|
35
- FileUtils.cp_r "#{path}/.", 'public'
35
+ FileUtils.cp_r "#{path}/.", 'public' unless File.expand_path("#{path}/.").eql? File.expand_path('public')
36
36
  end
37
37
 
38
38
  puts 'Preparing the Ditty migrations folder'
39
39
  Dir.mkdir 'migrations' unless File.exist?('migrations')
40
40
  ::Ditty::Components.migrations.each do |path|
41
- FileUtils.cp_r "#{path}/.", 'migrations'
41
+ FileUtils.cp_r "#{path}/.", 'migrations' unless File.expand_path("#{path}/.").eql? File.expand_path('migrations')
42
42
  end
43
43
  puts 'Migrations added:'
44
44
  Dir.foreach('migrations').sort.each { |x| puts x if File.file?("migrations/#{x}") }
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mail'
4
+ require 'ditty/services/logger'
5
+
6
+ module Ditty
7
+ module Services
8
+ module Email
9
+ CONFIG = './config/email.yml'.freeze
10
+
11
+ class << self
12
+ def method_missing(method, *args, &block)
13
+ return super unless respond_to_missing?(method)
14
+ config!
15
+ Mail.send(method, *args, &block)
16
+ end
17
+
18
+ def respond_to_missing?(method, _include_private = false)
19
+ Mail.respond_to? method
20
+ end
21
+
22
+ def config
23
+ @config ||= symbolize_keys File.exist?(CONFIG) ? YAML.load_file(CONFIG) : default
24
+ end
25
+
26
+ private
27
+
28
+ def config!
29
+ cfg = config
30
+ Mail.defaults do
31
+ delivery_method cfg[:delivery_method].to_sym, (cfg[:options] || {})
32
+ end
33
+ end
34
+
35
+ def default
36
+ { delivery_method: :logger, logger: Ditty::Services::Logger.instance }
37
+ end
38
+
39
+ def symbolize_keys(hash)
40
+ return hash.map { |v| symbolize_keys(v) } if hash.is_a? Array
41
+ return hash unless hash.is_a? Hash
42
+ Hash[hash.map { |k, v| [k.to_sym, symbolize_keys(v)] }]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/ditty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ditty
4
- VERSION = '0.3.1'.freeze
4
+ VERSION = '0.3.2'.freeze
5
5
  end
data/lib/ditty.rb CHANGED
@@ -33,6 +33,10 @@ module Ditty
33
33
  def inject(memo, &block)
34
34
  @mutex.synchronize { @hash.inject(memo, &block) }
35
35
  end
36
+
37
+ def each_with_object(memo, &block)
38
+ @mutex.synchronize { @hash.each_with_object(memo, &block) }
39
+ end
36
40
  end
37
41
 
38
42
  # Ripped off from Roda - https://github.com/jeremyevans/roda
@@ -68,19 +72,19 @@ module Ditty
68
72
 
69
73
  # Return a hash of controllers with their routes as keys: `{ '/users' => Ditty::Controllers::Users }`
70
74
  def self.routes
71
- components.inject({}) do |memo, comp|
75
+ rts = components.each_with_object({}) do |comp, memo|
72
76
  memo.merge! comp[1].routes if comp[1].respond_to?(:routes)
73
- memo
74
- end.compact
77
+ end
78
+ rts.compact
75
79
  end
76
80
 
77
81
  # Return an ordered list of navigation items:
78
82
  # `[{order:0, link:'/users/', text:'Users'}, {order:1, link:'/roles/', text:'Roles'}]
79
83
  def self.navigation
80
- components.inject([]) do |memo, comp|
84
+ nav = components.each_with_object([]) do |comp, memo|
81
85
  memo.concat comp[1].navigation if comp[1].respond_to?(:navigation)
82
- memo
83
- end.sort_by { |v| v[:order] }
86
+ end
87
+ nav.sort_by { |v| v[:order] }
84
88
  end
85
89
 
86
90
  def self.migrations
@@ -102,9 +106,8 @@ module Ditty
102
106
  end
103
107
 
104
108
  def self.workers
105
- components.inject([]) do |memo, comp|
109
+ components.each_with_object([]) do |comp, memo|
106
110
  memo.concat comp[1].workers if comp[1].respond_to?(:workers)
107
- memo
108
111
  end
109
112
  end
110
113
 
@@ -2,13 +2,25 @@
2
2
  - if list.pagination_record_count > 0
3
3
  %p.text-center Showing #{list.current_page_record_range} of #{list.pagination_record_count} records
4
4
  %ul.pager
5
- %li
6
- %a{href: "#{base_path}?page=1&count=#{list.page_size}"} First
7
- %li{class: ("disabled" if list.first_page?)}
8
- %a{href: prev_link} Previous
9
- %li{class: ("disabled" if list.last_page?)}
10
- %a{href: next_link} Next
11
- %li
12
- %a{href: "#{base_path}?page=#{list.page_count}&count=#{list.page_size}"} Last
5
+ - if list.first_page?
6
+ %li.disabled
7
+ %span First
8
+ %li.disabled
9
+ %span Previous
10
+ - else
11
+ %li
12
+ %a{href: first_link} First
13
+ %li
14
+ %a{href: prev_link} Previous
15
+ - if list.last_page?
16
+ %li.disabled
17
+ %span Next
18
+ %li.disabled
19
+ %span Last
20
+ - else
21
+ %li
22
+ %a{href: next_link} Next
23
+ %li
24
+ %a{href: last_link} Last
13
25
  - else
14
26
  %p.text-center No records to show
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ditty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jurgens du Toit
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-10-21 00:00:00.000000000 Z
11
+ date: 2017-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -352,6 +352,7 @@ files:
352
352
  - lib/ditty/helpers/views.rb
353
353
  - lib/ditty/helpers/wisper.rb
354
354
  - lib/ditty/listener.rb
355
+ - lib/ditty/middleware/error_catchall.rb
355
356
  - lib/ditty/models/audit_log.rb
356
357
  - lib/ditty/models/base.rb
357
358
  - lib/ditty/models/identity.rb
@@ -364,6 +365,7 @@ files:
364
365
  - lib/ditty/policies/user_policy.rb
365
366
  - lib/ditty/rake_tasks.rb
366
367
  - lib/ditty/seed.rb
368
+ - lib/ditty/services/email.rb
367
369
  - lib/ditty/services/logger.rb
368
370
  - lib/ditty/version.rb
369
371
  - migrate/20170207_base_tables.rb