ditty 0.3.1 → 0.3.2
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/.travis.yml +3 -0
- data/lib/ditty/controllers/application.rb +46 -6
- data/lib/ditty/controllers/component.rb +16 -10
- data/lib/ditty/controllers/users.rb +8 -8
- data/lib/ditty/helpers/authentication.rb +8 -4
- data/lib/ditty/helpers/component.rb +3 -3
- data/lib/ditty/helpers/views.rb +7 -3
- data/lib/ditty/listener.rb +4 -2
- data/lib/ditty/middleware/error_catchall.rb +20 -0
- data/lib/ditty/rake_tasks.rb +2 -2
- data/lib/ditty/services/email.rb +47 -0
- data/lib/ditty/version.rb +1 -1
- data/lib/ditty.rb +11 -8
- data/views/partials/pager.haml +20 -8
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b443ee45c38fca3c0f69379d0631b03f1f91263
|
4
|
+
data.tar.gz: 3b26f6324b6979e1e517c6338e41cb994dce80c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
|
63
|
-
|
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
|
-
|
68
|
-
|
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[
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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 =
|
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
|
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 "
|
62
|
+
redirect "#{base_path}/#{user.id}"
|
63
63
|
end
|
64
64
|
format.json do
|
65
65
|
headers 'Content-Type' => 'application/json'
|
66
|
-
redirect "
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
8
|
-
|
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[
|
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']
|
17
|
-
params['page']
|
16
|
+
count = params['count'] || 10
|
17
|
+
page = params['page'] || 1
|
18
18
|
|
19
|
-
dataset.select.paginate(
|
19
|
+
dataset.select.paginate(page.to_i, count.to_i)
|
20
20
|
end
|
21
21
|
|
22
22
|
def heading(action = nil)
|
data/lib/ditty/helpers/views.rb
CHANGED
@@ -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
|
-
|
52
|
-
|
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
|
}
|
data/lib/ditty/listener.rb
CHANGED
@@ -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
|
data/lib/ditty/rake_tasks.rb
CHANGED
@@ -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
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.
|
75
|
+
rts = components.each_with_object({}) do |comp, memo|
|
72
76
|
memo.merge! comp[1].routes if comp[1].respond_to?(:routes)
|
73
|
-
|
74
|
-
|
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.
|
84
|
+
nav = components.each_with_object([]) do |comp, memo|
|
81
85
|
memo.concat comp[1].navigation if comp[1].respond_to?(:navigation)
|
82
|
-
|
83
|
-
|
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.
|
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
|
|
data/views/partials/pager.haml
CHANGED
@@ -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
|
-
|
6
|
-
%
|
7
|
-
|
8
|
-
%
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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.
|
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-
|
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
|