ditty 0.8.0 → 0.9.0

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.pryrc +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/.travis.yml +5 -4
  6. data/CNAME +1 -0
  7. data/Dockerfile +18 -0
  8. data/Gemfile.ci +0 -2
  9. data/Rakefile +2 -2
  10. data/_config.yml +1 -0
  11. data/config.ru +4 -4
  12. data/ditty.gemspec +9 -3
  13. data/docs/CNAME +1 -0
  14. data/docs/_config.yml +1 -0
  15. data/docs/index.md +34 -0
  16. data/exe/ditty +2 -0
  17. data/lib/ditty.rb +4 -2
  18. data/lib/ditty/cli.rb +28 -4
  19. data/lib/ditty/components/{app.rb → ditty.rb} +19 -14
  20. data/lib/ditty/controllers/{application.rb → application_controller.rb} +58 -29
  21. data/lib/ditty/controllers/{audit_logs.rb → audit_logs_controller.rb} +2 -2
  22. data/lib/ditty/controllers/{auth.rb → auth_controller.rb} +17 -16
  23. data/lib/ditty/controllers/{component.rb → component_controller.rb} +19 -18
  24. data/lib/ditty/controllers/{main.rb → main_controller.rb} +6 -2
  25. data/lib/ditty/controllers/roles_controller.rb +23 -0
  26. data/lib/ditty/controllers/{user_login_traits.rb → user_login_traits_controller.rb} +4 -3
  27. data/lib/ditty/controllers/{users.rb → users_controller.rb} +11 -10
  28. data/lib/ditty/db.rb +4 -3
  29. data/lib/ditty/emails/base.rb +5 -2
  30. data/lib/ditty/generators/crud_generator.rb +104 -0
  31. data/lib/ditty/generators/migration_generator.rb +26 -0
  32. data/lib/ditty/generators/project_generator.rb +51 -0
  33. data/lib/ditty/helpers/component.rb +2 -1
  34. data/lib/ditty/helpers/pundit.rb +20 -4
  35. data/lib/ditty/helpers/response.rb +20 -13
  36. data/lib/ditty/helpers/views.rb +7 -3
  37. data/lib/ditty/listener.rb +5 -3
  38. data/lib/ditty/memcached.rb +8 -0
  39. data/lib/ditty/middleware/accept_extension.rb +2 -2
  40. data/lib/ditty/middleware/error_catchall.rb +2 -2
  41. data/lib/ditty/models/base.rb +4 -0
  42. data/lib/ditty/models/role.rb +1 -0
  43. data/lib/ditty/models/user.rb +14 -1
  44. data/lib/ditty/policies/role_policy.rb +1 -1
  45. data/lib/ditty/policies/user_login_trait_policy.rb +1 -1
  46. data/lib/ditty/services/authentication.rb +11 -10
  47. data/lib/ditty/services/email.rb +8 -4
  48. data/lib/ditty/services/logger.rb +1 -1
  49. data/lib/ditty/tasks/ditty.rake +17 -0
  50. data/lib/ditty/tasks/omniauth-ldap.rake +2 -2
  51. data/lib/ditty/templates/.gitignore +5 -0
  52. data/lib/ditty/templates/.rspec +2 -0
  53. data/lib/ditty/templates/.rubocop.yml +7 -0
  54. data/lib/ditty/templates/Rakefile +12 -0
  55. data/lib/ditty/templates/application.rb +12 -0
  56. data/lib/ditty/templates/config.ru +37 -0
  57. data/lib/ditty/templates/controller.rb.erb +58 -0
  58. data/lib/ditty/templates/env.example +4 -0
  59. data/lib/ditty/templates/lib/project.rb.erb +5 -0
  60. data/lib/ditty/templates/migration.rb.erb +7 -0
  61. data/lib/ditty/templates/model.rb.erb +26 -0
  62. data/lib/ditty/templates/pids/.empty_directory +0 -0
  63. data/lib/ditty/templates/policy.rb.erb +48 -0
  64. data/lib/ditty/templates/public/css/sb-admin-2.min.css +10 -0
  65. data/lib/ditty/templates/public/js/sb-admin-2.min.js +7 -0
  66. data/lib/ditty/templates/settings.yml.erb +18 -0
  67. data/lib/ditty/templates/sidekiq.rb +18 -0
  68. data/lib/ditty/templates/sidekiq.yml +9 -0
  69. data/lib/ditty/templates/spec_helper.rb +43 -0
  70. data/lib/ditty/templates/type.rb.erb +21 -0
  71. data/lib/ditty/templates/views/display.haml.tt +20 -0
  72. data/lib/ditty/templates/views/edit.haml.tt +10 -0
  73. data/lib/ditty/templates/views/form.haml.tt +11 -0
  74. data/lib/ditty/templates/views/index.haml.tt +29 -0
  75. data/lib/ditty/templates/views/new.haml.tt +10 -0
  76. data/lib/ditty/version.rb +1 -1
  77. data/lib/rubocop/cop/ditty/call_services_directly.rb +2 -2
  78. data/migrate/20181209_add_user_login_traits.rb +4 -4
  79. data/migrate/20190220_add_parent_id_to_roles.rb +9 -0
  80. data/public/css/styles.css +13 -0
  81. data/public/js/scripts.js +1 -0
  82. data/views/404.haml +2 -4
  83. data/views/audit_logs/index.haml +32 -34
  84. data/views/auth/forgot_password.haml +27 -16
  85. data/views/auth/identity.haml +14 -13
  86. data/views/auth/ldap.haml +2 -2
  87. data/views/auth/login.haml +22 -17
  88. data/views/auth/register.haml +19 -18
  89. data/views/auth/register_identity.haml +27 -12
  90. data/views/auth/reset_password.haml +2 -2
  91. data/views/blank.haml +42 -0
  92. data/views/index.haml +1 -1
  93. data/views/layout.haml +37 -30
  94. data/views/partials/content_tag.haml +0 -0
  95. data/views/partials/delete_form.haml +1 -1
  96. data/views/partials/filter_control.haml +1 -1
  97. data/views/partials/footer.haml +5 -5
  98. data/views/partials/form_control.haml +19 -12
  99. data/views/partials/navitems.haml +44 -0
  100. data/views/partials/notifications.haml +12 -8
  101. data/views/partials/pager.haml +17 -17
  102. data/views/partials/search.haml +6 -7
  103. data/views/partials/sidebar.haml +15 -37
  104. data/views/partials/topbar.haml +68 -0
  105. data/views/roles/display.haml +27 -6
  106. data/views/roles/edit.haml +3 -3
  107. data/views/roles/form.haml +1 -0
  108. data/views/roles/index.haml +23 -16
  109. data/views/roles/new.haml +2 -2
  110. data/views/user_login_traits/display.haml +4 -4
  111. data/views/user_login_traits/edit.haml +3 -3
  112. data/views/user_login_traits/index.haml +4 -4
  113. data/views/user_login_traits/new.haml +2 -2
  114. data/views/users/display.haml +11 -12
  115. data/views/users/edit.haml +3 -3
  116. data/views/users/form.haml +0 -0
  117. data/views/users/index.haml +31 -24
  118. data/views/users/login_traits.haml +6 -8
  119. data/views/users/new.haml +2 -2
  120. data/views/users/profile.haml +13 -13
  121. metadata +143 -15
  122. data/lib/ditty/controllers/roles.rb +0 -13
  123. data/views/partials/navbar.haml +0 -22
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/component'
3
+ require 'ditty/controllers/component_controller'
4
4
  require 'ditty/models/audit_log'
5
5
  require 'ditty/policies/audit_log_policy'
6
6
 
7
7
  module Ditty
8
- class AuditLogs < Ditty::Component
8
+ class AuditLogsController < ::Ditty::ComponentController
9
9
  set model_class: AuditLog
10
10
 
11
11
  SEARCHABLE = %i[details platform device browser ip_address].freeze
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/application'
3
+ require 'ditty/controllers/application_controller'
4
4
  require 'ditty/services/email'
5
5
  require 'securerandom'
6
6
 
7
7
  module Ditty
8
- class Auth < Application
8
+ class AuthController < ApplicationController
9
9
  set track_actions: true
10
10
 
11
11
  def redirect_path
12
12
  return "#{settings.map_path}/" if omniauth_redirect_path.nil?
13
- return "#{settings.map_path}/" if omniauth_redirect_path =~ %r{/#{settings.map_path}/auth/?}
13
+ return "#{settings.map_path}/" if omniauth_redirect_path.match? %r{/#{settings.map_path}/auth/?}
14
14
 
15
15
  omniauth_redirect_path
16
16
  end
@@ -67,7 +67,7 @@ module Ditty
67
67
  authorize ::Ditty::Identity, :login
68
68
  redirect settings.map_path if authenticated?
69
69
 
70
- haml :'auth/login', locals: { title: 'Log In' }
70
+ haml :'auth/login', locals: { title: 'Log In' }, layout: :blank
71
71
  end
72
72
 
73
73
  # Custom login form for LDAP to allow CSRF checks. Set the `request_path` for
@@ -76,13 +76,13 @@ module Ditty
76
76
  authorize ::Ditty::Identity, :login
77
77
  redirect settings.map_path if authenticated?
78
78
 
79
- haml :'auth/ldap', locals: { title: 'Company Log In' }
79
+ haml :'auth/ldap', locals: { title: 'Company Log In' }, layout: :blank
80
80
  end
81
81
 
82
82
  get '/forgot-password' do
83
83
  authorize ::Ditty::Identity, :forgot_password
84
84
 
85
- haml :'auth/forgot_password', locals: { title: 'Forgot your password?' }
85
+ haml :'auth/forgot_password', locals: { title: 'Forgot your password?' }, layout: :blank
86
86
  end
87
87
 
88
88
  post '/forgot-password' do
@@ -97,14 +97,14 @@ module Ditty
97
97
  identity.update(reset_token: token, reset_requested: Time.now)
98
98
  # Send Email
99
99
  reset_url = "#{request.base_url}#{settings.map_path}/reset-password?token=#{token}"
100
- Ditty::Services::Email.deliver(
100
+ ::Ditty::Services::Email.deliver(
101
101
  :forgot_password,
102
102
  email,
103
103
  locals: { identity: identity, reset_url: reset_url, request: request }
104
104
  )
105
105
  end
106
106
  flash[:info] = 'An email was sent to the email provided with instructions on how to reset your password'
107
- redirect '/login'
107
+ redirect "#{settings.map_path}/auth/login"
108
108
  end
109
109
 
110
110
  get '/reset-password' do
@@ -114,7 +114,7 @@ module Ditty
114
114
  identity = Identity[reset_token: params[:token]]
115
115
  halt 404 unless identity&.reset_requested && identity.reset_requested > (Time.now - (24 * 60 * 60))
116
116
 
117
- haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
117
+ haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }, layout: :blank
118
118
  end
119
119
 
120
120
  put '/reset-password' do
@@ -124,15 +124,15 @@ module Ditty
124
124
  halt 404 unless identity
125
125
  authorize identity, :reset_password
126
126
 
127
- identity_params = permitted_attributes(Identity, :update)
127
+ identity_params = permitted_parameters(Identity, :update)
128
128
  identity.set identity_params.merge(reset_token: nil, reset_requested: nil)
129
- if identity.valid? && identity.save
129
+ if identity.valid? && identity.save_changes
130
130
  broadcast(:identity_update_password, target: self)
131
131
  flash[:success] = 'Password Updated'
132
132
  redirect "#{settings.map_path}/auth/login"
133
133
  else
134
134
  broadcast(:identity_update_password_failed, target: self)
135
- haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }
135
+ haml :'auth/reset_password', locals: { title: 'Reset your password', identity: identity }, layout: :blank
136
136
  end
137
137
  end
138
138
 
@@ -141,7 +141,7 @@ module Ditty
141
141
  authorize ::Ditty::User.new, :register
142
142
 
143
143
  identity = Identity.new
144
- haml :'auth/register', locals: { title: 'Register', identity: identity }
144
+ haml :'auth/register', locals: { title: 'Register', identity: identity }, layout: :blank
145
145
  end
146
146
 
147
147
  # Register Action
@@ -152,8 +152,9 @@ module Ditty
152
152
  authorize user, :register
153
153
 
154
154
  begin
155
+ identity.valid?
155
156
  DB.transaction do
156
- user.save
157
+ user.save_changes
157
158
  user.add_identity identity
158
159
  broadcast(:user_register, target: self, values: { user: user })
159
160
  flash[:info] = 'Successfully Registered. Please log in'
@@ -161,7 +162,7 @@ module Ditty
161
162
  end
162
163
  rescue Sequel::ValidationFailed
163
164
  flash.now[:warning] = 'Could not complete the registration. Please try again.'
164
- haml :'auth/register', locals: { identity: identity }
165
+ haml :'auth/register', locals: { identity: identity }, layout: :blank
165
166
  end
166
167
  end
167
168
 
@@ -172,7 +173,7 @@ module Ditty
172
173
 
173
174
  halt 200 if request.xhr?
174
175
  flash[:info] = 'Logged Out'
175
- redirect(Ditty::Services::Settings[:logout_redirect_path] || "#{settings.map_path}/")
176
+ redirect(::Ditty::Services::Settings[:logout_redirect_path] || "#{settings.map_path}/")
176
177
  end
177
178
 
178
179
  # Unauthenticated
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/application'
3
+ require 'ditty/controllers/application_controller'
4
4
  require 'ditty/helpers/component'
5
5
  require 'ditty/helpers/response'
6
6
  require 'sinatra/json'
7
7
 
8
8
  module Ditty
9
- class Component < Application
9
+ class ComponentController < ApplicationController
10
10
  helpers Helpers::Component, Helpers::Response
11
11
 
12
12
  set base_path: nil
@@ -16,7 +16,12 @@ module Ditty
16
16
  set heading: nil
17
17
 
18
18
  def read(id)
19
- dataset.first(settings.model_class.primary_key => id)
19
+ dataset.with_pk(id)
20
+ end
21
+
22
+ def read!(id)
23
+ halt 404 unless (entity = read(id))
24
+ entity
20
25
  end
21
26
 
22
27
  def skip_verify!
@@ -25,7 +30,7 @@ module Ditty
25
30
 
26
31
  def trigger(event, attribs = {})
27
32
  attribs[:target] ||= self
28
- send(event, attribs) if self.respond_to? event
33
+ send(event, attribs) if respond_to? event
29
34
  broadcast(event, attribs)
30
35
  end
31
36
 
@@ -58,8 +63,7 @@ module Ditty
58
63
  get '/new/?' do
59
64
  authorize settings.model_class, :create
60
65
 
61
- entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
62
- session[:redirect_to] = request.fullpath
66
+ entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
63
67
  haml :"#{view_location}/new",
64
68
  locals: { entity: entity, title: heading(:new) },
65
69
  layout: layout
@@ -67,11 +71,11 @@ module Ditty
67
71
 
68
72
  # Create
69
73
  post '/' do
70
- entity = settings.model_class.new(permitted_attributes(settings.model_class, :create))
74
+ entity = settings.model_class.new(permitted_parameters(settings.model_class, :create))
71
75
  authorize entity, :create
72
76
 
73
77
  entity.db.transaction do
74
- entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
78
+ entity.save_changes # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
75
79
  trigger :component_create, entity: entity
76
80
  end
77
81
 
@@ -80,8 +84,7 @@ module Ditty
80
84
 
81
85
  # Read
82
86
  get '/:id/?' do |id|
83
- entity = read(id)
84
- halt 404 unless entity
87
+ entity = read!(id)
85
88
  authorize entity, :read
86
89
 
87
90
  trigger :component_read, entity: entity
@@ -90,10 +93,10 @@ module Ditty
90
93
 
91
94
  # Update Form
92
95
  get '/:id/edit/?' do |id|
93
- entity = read(id)
94
- halt 404 unless entity
96
+ entity = read!(id)
95
97
  authorize entity, :update
96
98
 
99
+ flash[:redirect_to] = "#{base_path}/#{entity.display_id}"
97
100
  haml :"#{view_location}/edit",
98
101
  locals: { entity: entity, title: heading(:edit) },
99
102
  layout: layout
@@ -101,13 +104,12 @@ module Ditty
101
104
 
102
105
  # Update
103
106
  put '/:id/?' do |id|
104
- entity = read(id)
105
- halt 404 unless entity
107
+ entity = read!(id)
106
108
  authorize entity, :update
107
109
 
108
110
  entity.db.transaction do
109
- entity.set(permitted_attributes(settings.model_class, :update))
110
- entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
111
+ entity.set(permitted_parameters(settings.model_class, :update))
112
+ entity.save_changes # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
111
113
  trigger :component_update, entity: entity
112
114
  end
113
115
 
@@ -115,8 +117,7 @@ module Ditty
115
117
  end
116
118
 
117
119
  delete '/:id/?' do |id|
118
- entity = read(id)
119
- halt 404 unless entity
120
+ entity = read!(id)
120
121
  authorize entity, :delete
121
122
 
122
123
  entity.db.transaction do
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/application'
3
+ require 'ditty/controllers/application_controller'
4
4
  require 'ditty/models/role'
5
5
  require 'ditty/models/user'
6
+ require 'ditty/services/settings'
6
7
 
7
8
  module Ditty
8
- class Main < Application
9
+ class MainController < ApplicationController
9
10
  set track_actions: true
10
11
 
11
12
  before '/' do
@@ -17,6 +18,9 @@ module Ditty
17
18
 
18
19
  # Home Page
19
20
  get '/' do
21
+ home_page = Services::Settings['ditty.home_page']
22
+ redirect "#{settings.map_path}#{home_page}" if home_page
23
+
20
24
  authenticate!
21
25
  haml :index, locals: { title: 'Home' }
22
26
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ditty/controllers/component_controller'
4
+ require 'ditty/models/role'
5
+ require 'ditty/policies/role_policy'
6
+
7
+ module Ditty
8
+ class RolesController < ::Ditty::ComponentController
9
+ SEARCHABLE = %i[name].freeze
10
+
11
+ set model_class: Role
12
+
13
+ helpers do
14
+ def parent_options(entity)
15
+ return policy_scope(::Ditty::Role) if entity.new?
16
+
17
+ policy_scope(::Ditty::Role)
18
+ .exclude(id: [entity.id] + entity.descendants.map(&:id))
19
+ .order(:name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/component'
3
+ require 'ditty/controllers/component_controller'
4
4
  require 'ditty/models/user_login_trait'
5
5
  require 'ditty/policies/user_login_trait_policy'
6
6
 
7
7
  module Ditty
8
- class UserLoginTraits < Ditty::Component
8
+ class UserLoginTraitsController < ::Ditty::ComponentController
9
9
  SEARCHABLE = %i[platform device browser ip_address].freeze
10
10
  FILTERS = [
11
11
  { name: :user, field: 'user.email' }
12
12
  ].freeze
13
13
 
14
- set base_path: 'login-traits'
14
+ set base_path: '/login-traits'
15
15
  set model_class: UserLoginTrait
16
+ set heading: 'Login'
16
17
  # set track_actions: true
17
18
  end
18
19
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ditty/controllers/component'
3
+ require 'ditty/controllers/component_controller'
4
4
  require 'ditty/models/user'
5
5
  require 'ditty/models/user_login_trait'
6
6
  require 'ditty/policies/user_policy'
@@ -8,7 +8,7 @@ require 'ditty/models/identity'
8
8
  require 'ditty/policies/identity_policy'
9
9
 
10
10
  module Ditty
11
- class Users < Ditty::Component
11
+ class UsersController < ::Ditty::ComponentController
12
12
  SEARCHABLE = %i[name surname email].freeze
13
13
 
14
14
  set model_class: User
@@ -28,8 +28,8 @@ module Ditty
28
28
 
29
29
  locals = { title: heading(:new) }
30
30
 
31
- user_params = permitted_attributes(User, :create)
32
- identity_params = permitted_attributes(Identity, :create)
31
+ user_params = permitted_parameters(User, :create)
32
+ identity_params = permitted_parameters(Identity, :create)
33
33
  user_params['email'] = identity_params['username']
34
34
  roles = user_params.delete('role_id')
35
35
 
@@ -38,7 +38,7 @@ module Ditty
38
38
 
39
39
  DB.transaction(isolation: :serializable) do
40
40
  begin
41
- identity.save
41
+ identity.save_changes
42
42
  rescue Sequel::ValidationFailed
43
43
  raise unless request.accept? 'text/html'
44
44
 
@@ -46,7 +46,7 @@ module Ditty
46
46
  locals = { title: heading(:new), entity: user, identity: identity }
47
47
  return haml(:"#{view_location}/new", locals: locals)
48
48
  end
49
- user.save
49
+ user.save_changes
50
50
  user.add_identity identity
51
51
 
52
52
  roles&.each do |role_id|
@@ -65,10 +65,10 @@ module Ditty
65
65
  halt 404 unless entity
66
66
  authorize entity, :update
67
67
 
68
- values = permitted_attributes(settings.model_class, :update)
68
+ values = permitted_parameters(settings.model_class, :update)
69
69
  roles = values.delete('role_id')
70
70
  entity.set values
71
- entity.save # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
71
+ entity.save_changes # Will trigger a Sequel::ValidationFailed exception if the model is incorrect
72
72
 
73
73
  if roles
74
74
  entity.remove_all_roles
@@ -94,9 +94,9 @@ module Ditty
94
94
  return redirect back
95
95
  end
96
96
 
97
- values = permitted_attributes(Identity, :create)
97
+ values = permitted_parameters(Identity, :create)
98
98
  identity.set values
99
- if identity.valid? && identity.save
99
+ if identity.valid? && identity.save_changes
100
100
  broadcast(:identity_update_password, target: self)
101
101
  flash[:success] = 'Password Updated'
102
102
  redirect back
@@ -129,6 +129,7 @@ module Ditty
129
129
  halt 404 unless entity
130
130
  authorize entity, :read
131
131
 
132
+ flash[:redirect_to] = request.path
132
133
  haml :"#{view_location}/profile", locals: { entity: entity, identity: entity.identity.first, title: 'My Account' }
133
134
  end
134
135
  end
@@ -8,7 +8,7 @@ require 'active_support/core_ext/object/blank'
8
8
  pool_timeout = (ENV['DB_POOL_TIMEOUT'] || 5).to_i
9
9
 
10
10
  if defined? DB
11
- Ditty::Services::Logger.instance.warn 'Database connection already set up'
11
+ ::Ditty::Services::Logger.warn 'Database connection already set up'
12
12
  elsif ENV['DATABASE_URL'].blank? == false
13
13
  # Delete DATABASE_URL from the environment, so it isn't accidently
14
14
  # passed to subprocesses. DATABASE_URL may contain passwords.
@@ -18,7 +18,7 @@ elsif ENV['DATABASE_URL'].blank? == false
18
18
  )
19
19
 
20
20
  DB.sql_log_level = (ENV['SEQUEL_LOGGING_LEVEL'] || :debug).to_sym
21
- DB.loggers << Ditty::Services::Logger.instance if ENV['DB_DEBUG'].to_i == 1
21
+ DB.loggers << ::Ditty::Services::Logger if ENV['DB_DEBUG'].to_i == 1
22
22
  DB.extension(:pagination)
23
23
  DB.extension(:schema_caching)
24
24
  DB.load_schema_cache?('./config/schema.dump')
@@ -26,6 +26,7 @@ elsif ENV['DATABASE_URL'].blank? == false
26
26
  Sequel::Model.plugin :validation_helpers
27
27
  Sequel::Model.plugin :update_or_create
28
28
  Sequel::Model.plugin :timestamps, update_on_create: true
29
+ Sequel::Model.plugin :auto_validations
29
30
  else
30
- Ditty::Services::Logger.instance.error 'No database connection set up'
31
+ ::Ditty::Services::Logger.error 'No database connection set up'
31
32
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'haml'
4
- require 'ditty/components/app'
4
+ require 'ditty/components/ditty'
5
5
 
6
6
  module Ditty
7
7
  module Emails
@@ -23,7 +23,10 @@ module Ditty
23
23
  @locals[param] ||= options[param]
24
24
  mail.send(param, options[param])
25
25
  end
26
- mail.body content
26
+ html = content
27
+ mail.html_part do
28
+ body html
29
+ end
27
30
  mail.deliver!
28
31
  end
29
32
 
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor/group'
4
+ require 'active_support/inflector'
5
+
6
+ module Ditty
7
+ module Generators
8
+ class CrudGenerator < Thor::Group
9
+ include Thor::Actions
10
+ attr_reader :namespace, :folder, :views_folder, :controller_name, :model_name, :policy_name # , :file_name
11
+
12
+ desc 'Create a CRUD Endpoint including the Model, Controller, Policy, Views and GraphQL Types'
13
+ argument :name, type: :string, desc: 'Name of the Model, eg. MyApp::User'
14
+
15
+ # --no-views make views optional
16
+ class_option :views, type: :boolean, default: true, desc: 'Generate views for controller'
17
+
18
+ def self.source_root
19
+ File.dirname(__FILE__)
20
+ end
21
+
22
+ def setup
23
+ @namespace = name.deconstantize
24
+ @folder = namespace.underscore
25
+ @model_name = name.demodulize
26
+ @views_folder = File.join('views', model_name.pluralize.underscore)
27
+ @controller_name = "#{model_name.pluralize}Controller"
28
+ @policy_name = "#{model_name}Policy"
29
+ end
30
+
31
+ def create_model
32
+ filename = File.join("lib/#{folder}/models", "#{model_name.underscore}.rb")
33
+ template '../templates/model.rb.erb', filename
34
+ end
35
+
36
+ def create_controller
37
+ filename = File.join("lib/#{folder}/controllers", "#{controller_name.underscore}.rb")
38
+ template '../templates/controller.rb.erb', filename
39
+ # TODO: Insert the route into the component file
40
+ # insert_into_file 'config.ru', "use #{class_name}\n", after: 'run ApplicationController\n'
41
+ end
42
+
43
+ def create_policy
44
+ filename = File.join("lib/#{folder}/policies", "#{policy_name.underscore}.rb")
45
+ template '../templates/policy.rb.erb', filename
46
+ end
47
+
48
+ def create_type
49
+ filename = File.join("lib/#{folder}/types", "#{model_name.underscore}_type.rb")
50
+ template '../templates/type.rb.erb', filename
51
+ end
52
+
53
+ def create_views
54
+ return unless options[:views]
55
+
56
+ directory '../templates/views', views_folder
57
+ end
58
+
59
+ private
60
+
61
+ def meta_columns
62
+ %i[id guid slug created_at updated_at]
63
+ end
64
+
65
+ def columns
66
+ require "#{folder}/models/#{model_name.underscore}"
67
+ name.constantize.columns
68
+ rescue StandardError
69
+ []
70
+ end
71
+
72
+ def schema
73
+ require "#{folder}/models/#{model_name.underscore}"
74
+ name.constantize.db_schema
75
+ rescue StandardError
76
+ []
77
+ end
78
+
79
+ def many_to_ones
80
+ DB.foreign_key_list(model_name.underscore.pluralize)
81
+ end
82
+
83
+ def name_column(table)
84
+ candidates = DB.schema(table.to_sym).to_h.keys - DB.foreign_key_list(table.to_sym).map { |e| e[:columns] }.flatten
85
+ (candidates - meta_columns).first
86
+ end
87
+
88
+ def graphql_types
89
+ @graphql_types ||= Hash.new('String').merge(
90
+ integer: 'Integer',
91
+ boolean: 'Boolean',
92
+ datetime: 'GraphQL::Types::ISO8601DateTime'
93
+ )
94
+ end
95
+
96
+ def input_types
97
+ @input_types ||= Hash.new('text').merge(
98
+ integer: 'number',
99
+ datetime: 'date'
100
+ )
101
+ end
102
+ end
103
+ end
104
+ end